diff options
126 files changed, 3824 insertions, 1851 deletions
diff --git a/Android.bp b/Android.bp index 2dc1cc3c19fe..13f6b6e9bc02 100644 --- a/Android.bp +++ b/Android.bp @@ -72,7 +72,7 @@ cc_library { subdirs = [ "cmds/*", - "core/jni", + "core/*", "libs/*", "media/*", "tools/*", diff --git a/Android.mk b/Android.mk index c87aec40a0ca..9890bb401404 100644 --- a/Android.mk +++ b/Android.mk @@ -128,7 +128,7 @@ LOCAL_SRC_FILES += \ ../../system/bt/binder/android/bluetooth/IBluetoothHeadsetPhone.aidl \ ../../system/bt/binder/android/bluetooth/IBluetoothHealth.aidl \ ../../system/bt/binder/android/bluetooth/IBluetoothHealthCallback.aidl \ - ../../system/bt/binder/android/bluetooth/IBluetoothInputDevice.aidl \ + ../../system/bt/binder/android/bluetooth/IBluetoothHidHost.aidl \ ../../system/bt/binder/android/bluetooth/IBluetoothPan.aidl \ ../../system/bt/binder/android/bluetooth/IBluetoothManager.aidl \ ../../system/bt/binder/android/bluetooth/IBluetoothManagerCallback.aidl \ @@ -139,7 +139,7 @@ LOCAL_SRC_FILES += \ ../../system/bt/binder/android/bluetooth/IBluetoothSap.aidl \ ../../system/bt/binder/android/bluetooth/IBluetoothStateChangeCallback.aidl \ ../../system/bt/binder/android/bluetooth/IBluetoothHeadsetClient.aidl \ - ../../system/bt/binder/android/bluetooth/IBluetoothInputHost.aidl \ + ../../system/bt/binder/android/bluetooth/IBluetoothHidDevice.aidl \ ../../system/bt/binder/android/bluetooth/IBluetoothHidDeviceCallback.aidl \ ../../system/bt/binder/android/bluetooth/IBluetoothGatt.aidl \ ../../system/bt/binder/android/bluetooth/IBluetoothGattCallback.aidl \ @@ -270,6 +270,7 @@ LOCAL_SRC_FILES += \ core/java/android/os/IRecoverySystemProgressListener.aidl \ core/java/android/os/IRemoteCallback.aidl \ core/java/android/os/ISchedulingPolicyService.aidl \ + core/java/android/os/IStatsCallbacks.aidl \ core/java/android/os/IStatsCompanionService.aidl \ core/java/android/os/IStatsManager.aidl \ core/java/android/os/IThermalEventListener.aidl \ diff --git a/api/current.txt b/api/current.txt index 8b3c74088df5..55f8751912e2 100644 --- a/api/current.txt +++ b/api/current.txt @@ -40445,6 +40445,8 @@ package android.telephony { field public static final int ENCODING_7BIT = 1; // 0x1 field public static final int ENCODING_8BIT = 2; // 0x2 field public static final int ENCODING_UNKNOWN = 0; // 0x0 + field public static final java.lang.String FORMAT_3GPP = "3gpp"; + field public static final java.lang.String FORMAT_3GPP2 = "3gpp2"; field public static final int MAX_USER_DATA_BYTES = 140; // 0x8c field public static final int MAX_USER_DATA_BYTES_WITH_HEADER = 134; // 0x86 field public static final int MAX_USER_DATA_SEPTETS = 160; // 0xa0 diff --git a/api/system-current.txt b/api/system-current.txt index ee23b6d92b71..c6a6fddf7d1b 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -43965,6 +43965,8 @@ package android.telephony { field public static final int ENCODING_7BIT = 1; // 0x1 field public static final int ENCODING_8BIT = 2; // 0x2 field public static final int ENCODING_UNKNOWN = 0; // 0x0 + field public static final java.lang.String FORMAT_3GPP = "3gpp"; + field public static final java.lang.String FORMAT_3GPP2 = "3gpp2"; field public static final int MAX_USER_DATA_BYTES = 140; // 0x8c field public static final int MAX_USER_DATA_BYTES_WITH_HEADER = 134; // 0x86 field public static final int MAX_USER_DATA_SEPTETS = 160; // 0xa0 diff --git a/api/system-removed.txt b/api/system-removed.txt index 639877fae6e2..e9afa701c755 100644 --- a/api/system-removed.txt +++ b/api/system-removed.txt @@ -330,7 +330,6 @@ package android.net.wifi { method public deprecated java.util.List<android.net.wifi.BatchedScanResult> getBatchedScanResults(); method public android.net.wifi.WifiConnectionStatistics getConnectionStatistics(); method public deprecated boolean isBatchedScanSupported(); - method public deprecated boolean setWifiApEnabled(android.net.wifi.WifiConfiguration, boolean); method public deprecated boolean startLocationRestrictedScan(android.os.WorkSource); } diff --git a/api/test-current.txt b/api/test-current.txt index 9a241add0bdb..5e55bc3baa55 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -32046,17 +32046,18 @@ package android.os { public static final class StrictMode.ViolationInfo implements android.os.Parcelable { ctor public StrictMode.ViolationInfo(); ctor public StrictMode.ViolationInfo(java.lang.Throwable, int); - ctor public StrictMode.ViolationInfo(java.lang.String, java.lang.Throwable, int); + ctor public deprecated StrictMode.ViolationInfo(java.lang.String, java.lang.Throwable, int); ctor public StrictMode.ViolationInfo(android.os.Parcel); ctor public StrictMode.ViolationInfo(android.os.Parcel, boolean); method public int describeContents(); method public void dump(android.util.Printer, java.lang.String); + method public java.lang.String getMessagePrefix(); + method public java.lang.String getStackTrace(); + method public java.lang.String getViolationDetails(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.os.StrictMode.ViolationInfo> CREATOR; field public java.lang.String broadcastIntentAction; - field public final android.app.ApplicationErrorReport.CrashInfo crashInfo; field public int durationMillis; - field public final java.lang.String message; field public int numAnimationsRunning; field public long numInstances; field public final int policy; @@ -40840,6 +40841,8 @@ package android.telephony { field public static final int ENCODING_7BIT = 1; // 0x1 field public static final int ENCODING_8BIT = 2; // 0x2 field public static final int ENCODING_UNKNOWN = 0; // 0x0 + field public static final java.lang.String FORMAT_3GPP = "3gpp"; + field public static final java.lang.String FORMAT_3GPP2 = "3gpp2"; field public static final int MAX_USER_DATA_BYTES = 140; // 0x8c field public static final int MAX_USER_DATA_BYTES_WITH_HEADER = 134; // 0x86 field public static final int MAX_USER_DATA_SEPTETS = 160; // 0xa0 diff --git a/cmds/incidentd/tests/Reporter_test.cpp b/cmds/incidentd/tests/Reporter_test.cpp index 5d074bcb0e4c..8d99fc7dbbbc 100644 --- a/cmds/incidentd/tests/Reporter_test.cpp +++ b/cmds/incidentd/tests/Reporter_test.cpp @@ -32,8 +32,6 @@ using namespace android::binder; using namespace std; using ::testing::StrEq; using ::testing::Test; -using ::testing::internal::CaptureStdout; -using ::testing::internal::GetCapturedStdout; class TestListener : public IIncidentReportStatusListener { @@ -139,20 +137,24 @@ TEST_F(ReporterTest, RunReportEmpty) { } TEST_F(ReporterTest, RunReportWithHeaders) { + TemporaryFile tf; IncidentReportArgs args1, args2; args1.addSection(1); args2.addSection(2); std::vector<int8_t> header {'a', 'b', 'c', 'd', 'e'}; args2.addHeader(header); - sp<ReportRequest> r1 = new ReportRequest(args1, l, STDOUT_FILENO); - sp<ReportRequest> r2 = new ReportRequest(args2, l, STDOUT_FILENO); + sp<ReportRequest> r1 = new ReportRequest(args1, l, tf.fd); + sp<ReportRequest> r2 = new ReportRequest(args2, l, tf.fd); reporter->batch.add(r1); reporter->batch.add(r2); - CaptureStdout(); ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport()); - EXPECT_THAT(GetCapturedStdout(), StrEq("\n\x5" "abcde")); + + string result; + ReadFileToString(tf.path, &result); + EXPECT_THAT(result, StrEq("\n\x5" "abcde")); + EXPECT_EQ(l->startInvoked, 2); EXPECT_EQ(l->finishInvoked, 2); EXPECT_TRUE(l->startSections.empty()); diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk index 8946aed58052..db634d4a6601 100644 --- a/cmds/statsd/Android.mk +++ b/cmds/statsd/Android.mk @@ -15,6 +15,7 @@ LOCAL_PATH:= $(call my-dir) statsd_common_src := \ + ../../core/java/android/os/IStatsCallbacks.aidl \ ../../core/java/android/os/IStatsCompanionService.aidl \ ../../core/java/android/os/IStatsManager.aidl \ src/stats_log.proto \ @@ -38,6 +39,7 @@ statsd_common_src := \ src/matchers/SimpleLogMatchingTracker.cpp \ src/metrics/CountAnomalyTracker.cpp \ src/metrics/MetricProducer.cpp \ + src/metrics/EventMetricProducer.cpp \ src/metrics/CountMetricProducer.cpp \ src/metrics/DurationMetricProducer.cpp \ src/metrics/MetricsManager.cpp \ @@ -65,7 +67,8 @@ statsd_common_shared_libraries := \ libselinux \ libutils \ libservices \ - libandroidfw + libandroidfw \ + libprotoutil # ========= # statsd diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index e7825cf75159..68f48a4f4c37 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -33,8 +33,9 @@ namespace android { namespace os { namespace statsd { -StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap) - : m_dropbox_writer("all-logs"), mUidMap(uidMap) { +StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap, + const std::function<void(const vector<uint8_t>&)>& pushLog) + : m_dropbox_writer("all-logs"), mUidMap(uidMap), mPushLog(pushLog) { } StatsLogProcessor::~StatsLogProcessor() { @@ -91,6 +92,41 @@ void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) { } } +void StatsLogProcessor::addEventMetricData(const EventMetricData& eventMetricData) { + // TODO: Replace this code when MetricsManager.onDumpReport() is ready to + // get a list of byte arrays. + flushIfNecessary(eventMetricData); + const int numBytes = eventMetricData.ByteSize(); + char buffer[numBytes]; + eventMetricData.SerializeToArray(&buffer[0], numBytes); + string bufferString(buffer, numBytes); + mEvents.push_back(bufferString); + mBufferSize += eventMetricData.ByteSize(); +} + +void StatsLogProcessor::flushIfNecessary(const EventMetricData& eventMetricData) { + if (eventMetricData.ByteSize() + mBufferSize > kMaxSerializedBytes) { + flush(); + } +} + +void StatsLogProcessor::flush() { + StatsLogReport logReport; + for (string eventBuffer : mEvents) { + EventMetricData eventFromBuffer; + eventFromBuffer.ParseFromString(eventBuffer); + EventMetricData* newEntry = logReport.mutable_event_metrics()->add_data(); + newEntry->CopyFrom(eventFromBuffer); + } + + const int numBytes = logReport.ByteSize(); + vector<uint8_t> logReportBuffer(numBytes); + logReport.SerializeToArray(&logReportBuffer[0], numBytes); + mPushLog(logReportBuffer); + mEvents.clear(); + mBufferSize = 0; +} + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index 3cefd29fa026..08090c11f724 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -33,7 +33,8 @@ namespace statsd { class StatsLogProcessor : public ConfigListener { public: - StatsLogProcessor(const sp<UidMap>& uidMap); + StatsLogProcessor(const sp<UidMap>& uidMap, + const std::function<void(const vector<uint8_t>&)>& pushLog); virtual ~StatsLogProcessor(); virtual void OnLogEvent(const LogEvent& event); @@ -44,6 +45,9 @@ public: // TODO: Once we have the ProtoOutputStream in c++, we can just return byte array. std::vector<StatsLogReport> onDumpReport(const ConfigKey& key); + /* Request a flush through a binder call. */ + void flush(); + private: // TODO: use EventMetrics to log the events. DropboxWriter m_dropbox_writer; @@ -51,6 +55,33 @@ private: std::unordered_map<ConfigKey, std::unique_ptr<MetricsManager>> mMetricsManagers; sp<UidMap> mUidMap; // Reference to the UidMap to lookup app name and version for each uid. + + /* Max *serialized* size of the logs kept in memory before flushing through binder call. + Proto lite does not implement the SpaceUsed() function which gives the in memory byte size. + So we cap memory usage by limiting the serialized size. Note that protobuf's in memory size + is higher than its serialized size. + */ + static const size_t kMaxSerializedBytes = 16 * 1024; + + /* List of data that was captured for a single metric over a given interval of time. */ + vector<string> mEvents; + + /* Current *serialized* size of the logs kept in memory. + To save computation, we will not calculate the size of the StatsLogReport every time when a + new entry is added, which would recursively call ByteSize() on every log entry. Instead, we + keep the sum of all individual stats log entry sizes. The size of a proto is approximately + the sum of the size of all member protos. + */ + size_t mBufferSize = 0; + + /* Check if the buffer size exceeds the max buffer size when the new entry is added, and flush + the logs to dropbox if true. */ + void flushIfNecessary(const EventMetricData& eventMetricData); + + /* Append event metric data to StatsLogReport. */ + void addEventMetricData(const EventMetricData& eventMetricData); + + std::function<void(const vector<uint8_t>&)> mPushLog; }; } // namespace statsd diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index 1faeee0a6554..604753ef54a0 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -68,7 +68,9 @@ StatsService::StatsService(const sp<Looper>& handlerLooper) mStatsPullerManager = new StatsPullerManager(); mUidMap = new UidMap(); mConfigManager = new ConfigManager(); - mProcessor = new StatsLogProcessor(mUidMap); + mProcessor = new StatsLogProcessor(mUidMap, [this](const vector<uint8_t>& log) { + pushLog(log); + }); mConfigManager->AddListener(mProcessor); @@ -507,6 +509,40 @@ void StatsService::OnLogEvent(const LogEvent& event) { mProcessor->OnLogEvent(event); } +Status StatsService::requestPush() { + mProcessor->flush(); + return Status::ok(); +} + +Status StatsService::pushLog(const vector<uint8_t>& log) { + std::lock_guard<std::mutex> lock(mLock); + for (size_t i = 0; i < mCallbacks.size(); i++) { + mCallbacks[i]->onReceiveLogs((vector<uint8_t>*)&log); + } + return Status::ok(); +} + +Status StatsService::subscribeStatsLog(const sp<IStatsCallbacks>& callback) { + std::lock_guard<std::mutex> lock(mLock); + for (size_t i = 0; i < mCallbacks.size(); i++) { + if (mCallbacks[i] == callback) { + return Status::fromStatusT(-errno); + } + } + mCallbacks.add(callback); + IInterface::asBinder(callback)->linkToDeath(this); + return Status::ok(); +} + +void StatsService::binderDied(const wp<IBinder>& who) { + for (size_t i = 0; i < mCallbacks.size(); i++) { + if (IInterface::asBinder(mCallbacks[i]) == who) { + mCallbacks.removeAt(i); + break; + } + } +} + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index 449a2b84bfd7..7f046584b2d0 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -24,6 +24,7 @@ #include "packages/UidMap.h" #include <android/os/BnStatsManager.h> +#include <android/os/IStatsCallbacks.h> #include <android/os/IStatsCompanionService.h> #include <binder/IResultReceiver.h> #include <binder/IShellCallback.h> @@ -42,7 +43,7 @@ namespace android { namespace os { namespace statsd { -class StatsService : public BnStatsManager, public LogListener { +class StatsService : public BnStatsManager, public LogListener, public IBinder::DeathRecipient { public: StatsService(const sp<Looper>& handlerLooper); virtual ~StatsService(); @@ -70,6 +71,22 @@ public: */ virtual void OnLogEvent(const LogEvent& event); + /** + * Binder call to force trigger pushLog. This would be called by callback + * clients. + */ + virtual Status requestPush() override; + + /** + * Pushes stats log entries from statsd to callback clients. + */ + Status pushLog(const vector<uint8_t>& log); + + /** + * Binder call to listen to statsd to send stats log entries. + */ + virtual Status subscribeStatsLog(const sp<IStatsCallbacks>& callbacks) override; + // TODO: public for testing since statsd doesn't run when system starts. Change to private // later. /** Inform statsCompanion that statsd is ready. */ @@ -78,6 +95,9 @@ public: /** Fetches and returns the StatsCompanionService. */ static sp<IStatsCompanionService> getStatsCompanionService(); + /** IBinder::DeathRecipient */ + virtual void binderDied(const wp<IBinder>& who) override; + private: /** * Load system properties at init. @@ -159,6 +179,16 @@ private: * Whether this is an eng build. */ bool mEngBuild; + + /** + * Lock for callback handling. + */ + std::mutex mLock; + + /** + * Vector maintaining the list of callbacks for clients. + */ + Vector< sp<IStatsCallbacks> > mCallbacks; }; } // namespace statsd diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp index bde3846ab66b..aff476814c94 100644 --- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp +++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#define DEBUG true // STOPSHIP if true +#define DEBUG false // STOPSHIP if true #include "Log.h" #include "SimpleConditionTracker.h" diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp index 038edd313081..c16971aeb8a8 100644 --- a/cmds/statsd/src/config/ConfigManager.cpp +++ b/cmds/statsd/src/config/ConfigManager.cpp @@ -120,7 +120,7 @@ static StatsdConfig build_fake_config() { int WAKE_LOCK_TAG_ID = 11; int WAKE_LOCK_UID_KEY_ID = 1; - int WAKE_LOCK_STATE_KEY = 2; + int WAKE_LOCK_STATE_KEY = 3; int WAKE_LOCK_ACQUIRE_VALUE = 1; int WAKE_LOCK_RELEASE_VALUE = 0; @@ -130,12 +130,12 @@ static StatsdConfig build_fake_config() { int APP_USAGE_FOREGROUND = 1; int APP_USAGE_BACKGROUND = 0; - int SCREEN_EVENT_TAG_ID = 2; + int SCREEN_EVENT_TAG_ID = 29; int SCREEN_EVENT_STATE_KEY = 1; int SCREEN_EVENT_ON_VALUE = 2; int SCREEN_EVENT_OFF_VALUE = 1; - int UID_PROCESS_STATE_TAG_ID = 3; + int UID_PROCESS_STATE_TAG_ID = 27; int UID_PROCESS_STATE_UID_KEY = 1; // Count Screen ON events. @@ -144,6 +144,12 @@ static StatsdConfig build_fake_config() { metric->set_what("SCREEN_TURNED_ON"); metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L); + // Anomaly threshold for screen-on count. + Alert* alert = metric->add_alerts(); + alert->set_number_of_buckets(6); + alert->set_trigger_if_sum_gt(10); + alert->set_refractory_period_secs(30); + // Count process state changes, slice by uid. metric = config.add_count_metric(); metric->set_metric_id(2); @@ -161,7 +167,7 @@ static StatsdConfig build_fake_config() { keyMatcher->set_key(UID_PROCESS_STATE_UID_KEY); metric->set_condition("SCREEN_IS_OFF"); - // Count wake lock, slice by uid, while SCREEN_IS_OFF and app in background + // Count wake lock, slice by uid, while SCREEN_IS_ON and app in background metric = config.add_count_metric(); metric->set_metric_id(4); metric->set_what("APP_GET_WL"); @@ -189,6 +195,11 @@ static StatsdConfig build_fake_config() { link->add_key_in_main()->set_key(WAKE_LOCK_UID_KEY_ID); link->add_key_in_condition()->set_key(APP_USAGE_UID_KEY_ID); + // Add an EventMetric to log process state change events. + EventMetric* eventMetric = config.add_event_metric(); + eventMetric->set_metric_id(6); + eventMetric->set_what("SCREEN_TURNED_ON"); + // Event matchers............ LogEntryMatcher* eventMatcher = config.add_log_entry_matcher(); eventMatcher->set_name("SCREEN_TURNED_ON"); diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp index fb992c11aad7..1a039f6d61c5 100644 --- a/cmds/statsd/src/logd/LogEvent.cpp +++ b/cmds/statsd/src/logd/LogEvent.cpp @@ -17,6 +17,7 @@ #include "logd/LogEvent.h" #include <sstream> +#include "stats_util.h" namespace android { namespace os { @@ -24,6 +25,7 @@ namespace statsd { using std::ostringstream; using std::string; +using android::util::ProtoOutputStream; // We need to keep a copy of the android_log_event_list owned by this instance so that the char* // for strings is not cleared before we can read them. @@ -203,30 +205,24 @@ string LogEvent::ToString() const { return result.str(); } -void LogEvent::ToProto(EventMetricData* out) const { - // TODO: Implement this when we have the ProtoOutputStream version. - - // set timestamp of the event. - out->set_timestamp_nanos(mTimestampNs); - - // uint64_t token = proto->StartObject(EventMetricData.FIELD); +void LogEvent::ToProto(ProtoOutputStream& proto) const { + long long atomToken = proto.start(TYPE_MESSAGE + mTagId); const size_t N = mElements.size(); for (size_t i=0; i<N; i++) { const int key = i + 1; const android_log_list_element& elem = mElements[i]; if (elem.type == EVENT_TYPE_INT) { - // proto->Write(key, elem.data.int32); + proto.write(TYPE_INT32 + key, elem.data.int32); } else if (elem.type == EVENT_TYPE_LONG) { - // proto->Write(key, elem.data.int64); + proto.write(TYPE_INT64 + key, (long long)elem.data.int64); } else if (elem.type == EVENT_TYPE_FLOAT) { - // proto->Write(key, elem.data.float32); + proto.write(TYPE_FLOAT + key, elem.data.float32); } else if (elem.type == EVENT_TYPE_STRING) { - // proto->Write(key, elem.data.string); + proto.write(TYPE_STRING + key, elem.data.string); } } - - //proto->EndObject(token); + proto.end(atomToken); } } // namespace statsd diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h index 410267507430..9ef20ea6968c 100644 --- a/cmds/statsd/src/logd/LogEvent.h +++ b/cmds/statsd/src/logd/LogEvent.h @@ -18,9 +18,10 @@ #include "frameworks/base/cmds/statsd/src/stats_log.pb.h" -#include <utils/Errors.h> +#include <android/util/ProtoOutputStream.h> #include <log/log_event_list.h> #include <log/log_read.h> +#include <utils/Errors.h> #include <memory> #include <string> @@ -80,10 +81,9 @@ public: string ToString() const; /** - * Write this object as an EventMetricData proto object. - * TODO: Use the streaming output generator to do this instead of this proto lite object? + * Write this object to a ProtoOutputStream. */ - void ToProto(EventMetricData* out) const; + void ToProto(android::util::ProtoOutputStream& out) const; /* * Get a KeyValuePair proto object. diff --git a/cmds/statsd/src/metrics/CountAnomalyTracker.cpp b/cmds/statsd/src/metrics/CountAnomalyTracker.cpp index e1c2b8b3a90e..7aa748fdeb77 100644 --- a/cmds/statsd/src/metrics/CountAnomalyTracker.cpp +++ b/cmds/statsd/src/metrics/CountAnomalyTracker.cpp @@ -17,23 +17,22 @@ #define DEBUG true // STOPSHIP if true #include "Log.h" -#define VLOG(...) \ - if (DEBUG) ALOGD(__VA_ARGS__); - #include "CountAnomalyTracker.h" +#include <time.h> + namespace android { namespace os { namespace statsd { -CountAnomalyTracker::CountAnomalyTracker(size_t numBuckets, int thresholdGt) - : mNumPastBuckets(numBuckets > 0 ? numBuckets - 1 : 0), - mPastBuckets(mNumPastBuckets > 0 ? (new int[mNumPastBuckets]) : nullptr), - mThresholdGt(thresholdGt) { +CountAnomalyTracker::CountAnomalyTracker(const Alert& alert) + : mAlert(alert), + mNumPastBuckets(alert.number_of_buckets() > 0 ? alert.number_of_buckets() - 1 : 0), + mPastBuckets(mNumPastBuckets > 0 ? (new int[mNumPastBuckets]) : nullptr) { VLOG("CountAnomalyTracker() called"); - if (numBuckets < 1) { - ALOGE("Cannot create CountAnomalyTracker with %zu buckets", numBuckets); + if (alert.number_of_buckets() < 1) { + ALOGE("Cannot create CountAnomalyTracker with %d buckets", alert.number_of_buckets()); } reset(); // initialization } @@ -84,22 +83,45 @@ void CountAnomalyTracker::reset() { } void CountAnomalyTracker::checkAnomaly(int currentCount) { - // Note that this works even if mNumPastBuckets < 1 (since then - // mSumPastCounters = 0 so the comparison is based only on currentCount). + // Skip the check if in refractory period. + if (time(nullptr) < mRefractoryPeriodEndsSec) { + VLOG("Skipping anomaly check since within refractory period"); + return; + } // TODO: Remove these extremely verbose debugging log. - VLOG("Checking whether %d + %d > %d", - mSumPastCounters, currentCount, mThresholdGt); + VLOG("Checking whether %d + %d > %lld", + mSumPastCounters, currentCount, mAlert.trigger_if_sum_gt()); - if (mSumPastCounters + currentCount > mThresholdGt) { + // Note that this works even if mNumPastBuckets < 1 (since then + // mSumPastCounters = 0 so the comparison is based only on currentCount). + if (mAlert.has_trigger_if_sum_gt() && + mSumPastCounters + currentCount > mAlert.trigger_if_sum_gt()) { declareAnomaly(); } } void CountAnomalyTracker::declareAnomaly() { - // TODO: check that not in refractory period. - // TODO: Do something. - ALOGI("An anomaly has occurred!"); + // TODO(guardrail): Consider guarding against too short refractory periods. + time_t currTime = time(nullptr); + mRefractoryPeriodEndsSec = currTime + mAlert.refractory_period_secs(); + + // TODO: If we had access to the bucket_size_millis, consider calling reset() + // if (mAlert.refractory_period_secs() > mNumPastBuckets * bucket_size_millis * 1000). + + if (mAlert.has_incidentd_details()) { + const Alert_IncidentdDetails& incident = mAlert.incidentd_details(); + if (incident.has_alert_name()) { + ALOGW("An anomaly (%s) has occurred! Informing incidentd.", + incident.alert_name().c_str()); + } else { + // TODO: Can construct a name based on the criteria (and/or relay the criteria). + ALOGW("An anomaly (nameless) has occurred! Informing incidentd."); + } + // TODO: Send incidentd_details.name and incidentd_details.incidentd_sections to incidentd + } else { + ALOGW("An anomaly has occurred! (But informing incidentd not requested.)"); + } } } // namespace statsd diff --git a/cmds/statsd/src/metrics/CountAnomalyTracker.h b/cmds/statsd/src/metrics/CountAnomalyTracker.h index 449dee90e0a6..13c1ccd0816c 100644 --- a/cmds/statsd/src/metrics/CountAnomalyTracker.h +++ b/cmds/statsd/src/metrics/CountAnomalyTracker.h @@ -17,8 +17,10 @@ #ifndef COUNT_ANOMALY_TRACKER_H #define COUNT_ANOMALY_TRACKER_H -#include <stdlib.h> +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert + #include <memory> // unique_ptr +#include <stdlib.h> namespace android { namespace os { @@ -26,7 +28,7 @@ namespace statsd { class CountAnomalyTracker { public: - CountAnomalyTracker(size_t numBuckets, int thresholdGt); + CountAnomalyTracker(const Alert& alert); virtual ~CountAnomalyTracker(); @@ -43,6 +45,9 @@ public: void checkAnomaly(int currentCount); private: + // statsd_config.proto Alert message that defines this tracker. + const Alert mAlert; + // Number of past buckets. One less than the total number of buckets needed // for the anomaly detection (since the current bucket is not in the past). const size_t mNumPastBuckets; @@ -59,8 +64,9 @@ private: // Index of the oldest bucket (i.e. the next bucket to be overwritten). size_t mOldestBucketIndex = 0; - // If mSumPastCounters + currentCount > mThresholdGt --> Anomaly! - const int mThresholdGt; + // Timestamp that the refractory period (if this anomaly was declared) ends, in seconds. + // If an anomaly was never declared, set to 0. + time_t mRefractoryPeriodEndsSec = 0; void declareAnomaly(); diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp index 28cb503e2bc8..7bb9c8a502c1 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.cpp +++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp @@ -39,9 +39,7 @@ CountMetricProducer::CountMetricProducer(const CountMetric& metric, const int co const sp<ConditionWizard>& wizard) // TODO: Pass in the start time from MetricsManager, instead of calling time() here. : MetricProducer((time(nullptr) * NANO_SECONDS_IN_A_SECOND), conditionIndex, wizard), - mMetric(metric), - // TODO: read mAnomalyTracker parameters from config file. - mAnomalyTracker(6, 10) { + mMetric(metric) { // TODO: evaluate initial conditions. and set mConditionMet. if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) { mBucketSizeNs = metric.bucket().bucket_size_millis() * 1000 * 1000; @@ -49,6 +47,17 @@ CountMetricProducer::CountMetricProducer(const CountMetric& metric, const int co mBucketSizeNs = LLONG_MAX; } + mAnomalyTrackers.reserve(metric.alerts_size()); + for (int i = 0; i < metric.alerts_size(); i++) { + const Alert& alert = metric.alerts(i); + if (alert.trigger_if_sum_gt() > 0 && alert.number_of_buckets() > 0) { + mAnomalyTrackers.push_back(std::make_unique<CountAnomalyTracker>(alert)); + } else { + ALOGW("Ignoring invalid count metric alert: threshold=%lld num_buckets= %d", + alert.trigger_if_sum_gt(), alert.number_of_buckets()); + } + } + // TODO: use UidMap if uid->pkg_name is required mDimension.insert(mDimension.begin(), metric.dimension().begin(), metric.dimension().end()); @@ -148,6 +157,11 @@ void CountMetricProducer::onMatchedLogEventInternal( count++; } + // TODO: Re-add anomaly detection (similar to): + // for (auto& tracker : mAnomalyTrackers) { + // tracker->checkAnomaly(mCounter); + // } + VLOG("metric %lld %s->%d", mMetric.metric_id(), eventKey.c_str(), mCurrentSlicedCounter[eventKey]); } @@ -176,6 +190,11 @@ void CountMetricProducer::flushCounterIfNeeded(const uint64_t eventTimeNs) { counter.second); } + // TODO: Re-add anomaly detection (similar to): + // for (auto& tracker : mAnomalyTrackers) { + // tracker->addPastBucket(mCounter, numBucketsForward); + //} + // Reset counters mCurrentSlicedCounter.clear(); diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h index 8bbdb016666a..340c8309b7fa 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.h +++ b/cmds/statsd/src/metrics/CountMetricProducer.h @@ -60,14 +60,14 @@ protected: private: const CountMetric mMetric; - CountAnomalyTracker mAnomalyTracker; - // Save the past buckets and we can clear when the StatsLogReport is dumped. std::unordered_map<HashableDimensionKey, std::vector<CountBucketInfo>> mPastBuckets; // The current bucket. std::unordered_map<HashableDimensionKey, int> mCurrentSlicedCounter; + vector<unique_ptr<CountAnomalyTracker>> mAnomalyTrackers; + void flushCounterIfNeeded(const uint64_t newEventTime); }; diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp new file mode 100644 index 000000000000..8b3f405eec80 --- /dev/null +++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp @@ -0,0 +1,132 @@ +/* + * 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. + */ + +#define DEBUG true // STOPSHIP if true +#include "Log.h" + +#include "EventMetricProducer.h" +#include "stats_util.h" + +#include <cutils/log.h> +#include <limits.h> +#include <stdlib.h> + +using android::util::ProtoOutputStream; +using std::map; +using std::string; +using std::unordered_map; +using std::vector; + +namespace android { +namespace os { +namespace statsd { + +// for StatsLogReport +const int FIELD_ID_METRIC_ID = 1; +const int FIELD_ID_START_REPORT_NANOS = 2; +const int FIELD_ID_END_REPORT_NANOS = 2; +const int FIELD_ID_EVENT_METRICS = 4; +// for EventMetricData +const int FIELD_ID_TIMESTAMP_NANOS = 1; +const int FIELD_ID_STATS_EVENTS = 2; +// for CountMetricDataWrapper +const int FIELD_ID_DATA = 1; + +EventMetricProducer::EventMetricProducer(const EventMetric& metric, const int conditionIndex, + const sp<ConditionWizard>& wizard) + // TODO: Pass in the start time from MetricsManager, instead of calling time() here. + : MetricProducer((time(nullptr) * NANO_SECONDS_IN_A_SECOND), conditionIndex, wizard), + mMetric(metric) { + if (metric.links().size() > 0) { + mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(), + metric.links().end()); + mConditionSliced = true; + } + + startNewProtoOutputStream(mStartTimeNs); + + VLOG("metric %lld created. bucket size %lld start_time: %lld", metric.metric_id(), + (long long)mBucketSizeNs, (long long)mStartTimeNs); +} + +EventMetricProducer::~EventMetricProducer() { + VLOG("~EventMetricProducer() called"); +} + +void EventMetricProducer::startNewProtoOutputStream(long long startTime) { + mProto = std::make_unique<ProtoOutputStream>(); + // TODO: We need to auto-generate the field IDs for StatsLogReport, EventMetricData, + // and StatsEvent. + mProto->write(TYPE_INT32 + FIELD_ID_METRIC_ID, mMetric.metric_id()); + mProto->write(TYPE_INT64 + FIELD_ID_START_REPORT_NANOS, startTime); + mProtoToken = mProto->start(TYPE_MESSAGE + FIELD_ID_EVENT_METRICS); +} + +void EventMetricProducer::finish() { +} + +void EventMetricProducer::onSlicedConditionMayChange() { +} + +StatsLogReport EventMetricProducer::onDumpReport() { + long long endTime = time(nullptr) * NANO_SECONDS_IN_A_SECOND; + mProto->end(mProtoToken); + mProto->write(TYPE_INT64 + FIELD_ID_END_REPORT_NANOS, endTime); + + size_t bufferSize = mProto->size(); + VLOG("metric %lld dump report now... proto size: %zu ", mMetric.metric_id(), bufferSize); + std::unique_ptr<uint8_t[]> buffer(new uint8_t[bufferSize]); + size_t pos = 0; + auto it = mProto->data(); + while (it.readBuffer() != NULL) { + size_t toRead = it.currentToRead(); + std::memcpy(&buffer[pos], it.readBuffer(), toRead); + pos += toRead; + it.rp()->move(toRead); + } + + startNewProtoOutputStream(endTime); + + // TODO: Once we migrate all MetricProducers to use ProtoOutputStream, we should return this: + // return std::move(buffer); + return StatsLogReport(); +} + +void EventMetricProducer::onConditionChanged(const bool conditionMet) { + VLOG("Metric %lld onConditionChanged", mMetric.metric_id()); + mCondition = conditionMet; +} + +void EventMetricProducer::onMatchedLogEventInternal( + const size_t matcherIndex, const HashableDimensionKey& eventKey, + const std::map<std::string, HashableDimensionKey>& conditionKey, bool condition, + const LogEvent& event) { + + if (!condition) { + return; + } + + long long wrapperToken = mProto->start(TYPE_MESSAGE + FIELD_ID_DATA); + mProto->write(TYPE_INT64 + FIELD_ID_TIMESTAMP_NANOS, (long long)event.GetTimestampNs()); + long long eventToken = mProto->start(TYPE_MESSAGE + FIELD_ID_STATS_EVENTS); + event.ToProto(*mProto); + mProto->end(eventToken); + mProto->end(wrapperToken); +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h new file mode 100644 index 000000000000..879175cc25ff --- /dev/null +++ b/cmds/statsd/src/metrics/EventMetricProducer.h @@ -0,0 +1,70 @@ +/* + * 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 EVENT_METRIC_PRODUCER_H +#define EVENT_METRIC_PRODUCER_H + +#include <unordered_map> + +#include <android/util/ProtoOutputStream.h> +#include "../condition/ConditionTracker.h" +#include "../matchers/matcher_util.h" +#include "MetricProducer.h" +#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" +#include "stats_util.h" + +namespace android { +namespace os { +namespace statsd { + +class EventMetricProducer : public MetricProducer { +public: + // TODO: Pass in the start time from MetricsManager, it should be consistent for all metrics. + EventMetricProducer(const EventMetric& eventMetric, const int conditionIndex, + const sp<ConditionWizard>& wizard); + + virtual ~EventMetricProducer(); + + void onMatchedLogEventInternal(const size_t matcherIndex, const HashableDimensionKey& eventKey, + const std::map<std::string, HashableDimensionKey>& conditionKey, + bool condition, const LogEvent& event) override; + + void onConditionChanged(const bool conditionMet) override; + + void finish() override; + + StatsLogReport onDumpReport() override; + + void onSlicedConditionMayChange() override; + + // TODO: Implement this later. + virtual void notifyAppUpgrade(const string& apk, const int uid, const int version) override{}; + +private: + const EventMetric mMetric; + + std::unique_ptr<android::util::ProtoOutputStream> mProto; + + long long mProtoToken; + + void startNewProtoOutputStream(long long timestamp); +}; + +} // namespace statsd +} // namespace os +} // namespace android +#endif // EVENT_METRIC_PRODUCER_H diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index c19d46298de5..1ffa58b8c862 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -43,7 +43,7 @@ MetricsManager::MetricsManager(const StatsdConfig& config) { } MetricsManager::~MetricsManager() { - VLOG("~MetricManager()"); + VLOG("~MetricsManager()"); } bool MetricsManager::isConfigValid() const { diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index 56f57d3d3654..2f91460061fa 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -51,11 +51,11 @@ private: std::set<int> mTagIds; // We only store the sp of LogMatchingTracker, MetricProducer, and ConditionTracker in - // MetricManager. There are relationship between them, and the relationship are denoted by index - // instead of pointers. The reasons for this are: (1) the relationship between them are - // complicated, store index instead of pointers reduce the risk of A holds B's sp, and B holds - // A's sp. (2) When we evaluate matcher results, or condition results, we can quickly get the - // related results from a cache using the index. + // MetricsManager. There are relationships between them, and the relationships are denoted by + // index instead of pointers. The reasons for this are: (1) the relationship between them are + // complicated, so storing index instead of pointers reduces the risk that A holds B's sp, and B + // holds A's sp. (2) When we evaluate matcher results, or condition results, we can quickly get + // the related results from a cache using the index. // Hold all the log entry matchers from the config. std::vector<sp<LogMatchingTracker>> mAllLogEntryMatchers; diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp index 23071aa0899c..e90f998a7179 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp @@ -20,6 +20,7 @@ #include "../matchers/SimpleLogMatchingTracker.h" #include "CountMetricProducer.h" #include "DurationMetricProducer.h" +#include "EventMetricProducer.h" #include "stats_util.h" using std::set; @@ -31,13 +32,51 @@ namespace android { namespace os { namespace statsd { -int getTrackerIndex(const string& name, const unordered_map<string, int>& logTrackerMap) { - auto logTrackerIt = logTrackerMap.find(name); +bool handleMetricWithLogTrackers(const string what, const int metricIndex, + const unordered_map<string, int>& logTrackerMap, + unordered_map<int, std::vector<int>>& trackerToMetricMap, + int& logTrackerIndex) { + auto logTrackerIt = logTrackerMap.find(what); if (logTrackerIt == logTrackerMap.end()) { - ALOGW("cannot find the LogEventMatcher %s in config", name.c_str()); - return MATCHER_NOT_FOUND; + ALOGW("cannot find the LogEntryMatcher %s in config", what.c_str()); + return false; + } + logTrackerIndex = logTrackerIt->second; + auto& metric_list = trackerToMetricMap[logTrackerIndex]; + metric_list.push_back(metricIndex); + return true; +} + +bool handleMetricWithConditions( + const string condition, const int metricIndex, + const unordered_map<string, int>& conditionTrackerMap, + const ::google::protobuf::RepeatedPtrField<::android::os::statsd::EventConditionLink>& + links, + vector<sp<ConditionTracker>>& allConditionTrackers, int& conditionIndex, + unordered_map<int, std::vector<int>>& conditionToMetricMap) { + auto condition_it = conditionTrackerMap.find(condition); + if (condition_it == conditionTrackerMap.end()) { + ALOGW("cannot find the Condition %s in the config", condition.c_str()); + return false; } - return logTrackerIt->second; + + for (const auto& link : links) { + auto it = conditionTrackerMap.find(link.condition()); + if (it == conditionTrackerMap.end()) { + ALOGW("cannot find the Condition %s in the config", link.condition().c_str()); + return false; + } + allConditionTrackers[condition_it->second]->setSliced(true); + allConditionTrackers[it->second]->setSliced(true); + allConditionTrackers[it->second]->addDimensions( + vector<KeyMatcher>(link.key_in_condition().begin(), link.key_in_condition().end())); + } + conditionIndex = condition_it->second; + + // will create new vector if not exist before. + auto& metricList = conditionToMetricMap[condition_it->second]; + metricList.push_back(metricIndex); + return true; } bool initLogTrackers(const StatsdConfig& config, unordered_map<string, int>& logTrackerMap, @@ -142,7 +181,8 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l unordered_map<int, std::vector<int>>& conditionToMetricMap, unordered_map<int, std::vector<int>>& trackerToMetricMap) { sp<ConditionWizard> wizard = new ConditionWizard(allConditionTrackers); - const int allMetricsCount = config.count_metric_size() + config.duration_metric_size(); + const int allMetricsCount = + config.count_metric_size() + config.duration_metric_size() + config.event_metric_size(); allMetricProducers.reserve(allMetricsCount); // Build MetricProducers for each metric defined in config. @@ -155,100 +195,52 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l } int metricIndex = allMetricProducers.size(); - - auto logTrackerIt = logTrackerMap.find(metric.what()); - if (logTrackerIt == logTrackerMap.end()) { - ALOGW("cannot find the LogEntryMatcher %s in config", metric.what().c_str()); + int trackerIndex; + if (!handleMetricWithLogTrackers(metric.what(), metricIndex, logTrackerMap, + trackerToMetricMap, trackerIndex)) { return false; } - int logTrackerIndex = logTrackerIt->second; - auto& metric_list = trackerToMetricMap[logTrackerIndex]; - metric_list.push_back(metricIndex); - - sp<MetricProducer> countProducer; + int conditionIndex = -1; if (metric.has_condition()) { - auto condition_it = conditionTrackerMap.find(metric.condition()); - if (condition_it == conditionTrackerMap.end()) { - ALOGW("cannot find the Condition %s in the config", metric.condition().c_str()); - return false; - } - - for (const auto& link : metric.links()) { - auto it = conditionTrackerMap.find(link.condition()); - if (it == conditionTrackerMap.end()) { - ALOGW("cannot find the Condition %s in the config", link.condition().c_str()); - return false; - } - allConditionTrackers[condition_it->second]->setSliced(true); - allConditionTrackers[it->second]->setSliced(true); - allConditionTrackers[it->second]->addDimensions(vector<KeyMatcher>( - link.key_in_condition().begin(), link.key_in_condition().end())); - } - - countProducer = new CountMetricProducer(metric, condition_it->second, wizard); - // will create new vector if not exist before. - auto& metricList = conditionToMetricMap[condition_it->second]; - metricList.push_back(metricIndex); - } else { - countProducer = new CountMetricProducer(metric, -1 /*no condition*/, wizard); + handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, + metric.links(), allConditionTrackers, conditionIndex, + conditionToMetricMap); } + + sp<MetricProducer> countProducer = new CountMetricProducer(metric, conditionIndex, wizard); allMetricProducers.push_back(countProducer); } for (int i = 0; i < config.duration_metric_size(); i++) { int metricIndex = allMetricProducers.size(); - const DurationMetric metric = config.duration_metric(i); - if (!metric.has_start()) { - ALOGW("cannot find start in DurationMetric %lld", metric.metric_id()); + const DurationMetric& metric = config.duration_metric(i); + int trackerIndices[3] = {-1, -1, -1}; + if (!metric.has_start() || + !handleMetricWithLogTrackers(metric.start(), metricIndex, logTrackerMap, + trackerToMetricMap, trackerIndices[0])) { + ALOGE("Duration metrics must specify a valid the start event matcher"); return false; } - int trackerIndices[] = {-1, -1, -1}; - trackerIndices[0] = getTrackerIndex(metric.start(), logTrackerMap); - - if (metric.has_stop()) { - trackerIndices[1] = getTrackerIndex(metric.stop(), logTrackerMap); - } - - if (metric.has_stop_all()) { - trackerIndices[2] = getTrackerIndex(metric.stop_all(), logTrackerMap); + if (metric.has_stop() && + !handleMetricWithLogTrackers(metric.stop(), metricIndex, logTrackerMap, + trackerToMetricMap, trackerIndices[1])) { + return false; } - for (const int& index : trackerIndices) { - if (index == MATCHER_NOT_FOUND) { - return false; - } - if (index >= 0) { - auto& metric_list = trackerToMetricMap[index]; - metric_list.push_back(metricIndex); - } + if (metric.has_stop_all() && + !handleMetricWithLogTrackers(metric.stop_all(), metricIndex, logTrackerMap, + trackerToMetricMap, trackerIndices[2])) { + return false; } int conditionIndex = -1; if (metric.has_predicate()) { - auto condition_it = conditionTrackerMap.find(metric.predicate()); - if (condition_it == conditionTrackerMap.end()) { - ALOGW("cannot find the Condition %s in the config", metric.predicate().c_str()); - return false; - } - conditionIndex = condition_it->second; - - for (const auto& link : metric.links()) { - auto it = conditionTrackerMap.find(link.condition()); - if (it == conditionTrackerMap.end()) { - ALOGW("cannot find the Condition %s in the config", link.condition().c_str()); - return false; - } - allConditionTrackers[condition_it->second]->setSliced(true); - allConditionTrackers[it->second]->setSliced(true); - allConditionTrackers[it->second]->addDimensions(vector<KeyMatcher>( - link.key_in_condition().begin(), link.key_in_condition().end())); - } - - auto& metricList = conditionToMetricMap[conditionIndex]; - metricList.push_back(metricIndex); + handleMetricWithConditions(metric.predicate(), metricIndex, conditionTrackerMap, + metric.links(), allConditionTrackers, conditionIndex, + conditionToMetricMap); } sp<MetricProducer> durationMetric = @@ -257,6 +249,32 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l allMetricProducers.push_back(durationMetric); } + + for (int i = 0; i < config.event_metric_size(); i++) { + int metricIndex = allMetricProducers.size(); + const EventMetric& metric = config.event_metric(i); + if (!metric.has_metric_id() || !metric.has_what()) { + ALOGW("cannot find the metric id or what in config"); + return false; + } + int trackerIndex; + if (!handleMetricWithLogTrackers(metric.what(), metricIndex, logTrackerMap, + trackerToMetricMap, trackerIndex)) { + return false; + } + + int conditionIndex = -1; + if (metric.has_condition()) { + handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, + metric.links(), allConditionTrackers, conditionIndex, + conditionToMetricMap); + } + + sp<MetricProducer> eventMetric = new EventMetricProducer(metric, conditionIndex, wizard); + + allMetricProducers.push_back(eventMetric); + } + return true; } diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h index 38149a6aecd6..6722eb3cfe72 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.h +++ b/cmds/statsd/src/metrics/metrics_manager_util.h @@ -22,7 +22,6 @@ #include "../condition/ConditionTracker.h" #include "../matchers/LogMatchingTracker.h" -#include "CountMetricProducer.h" namespace android { namespace os { @@ -81,7 +80,7 @@ bool initMetrics( std::unordered_map<int, std::vector<int>>& conditionToMetricMap, std::unordered_map<int, std::vector<int>>& trackerToMetricMap); -// Initialize MetricManager from StatsdConfig. +// Initialize MetricsManager from StatsdConfig. // Parameters are the members of MetricsManager. See MetricsManager for declaration. bool initStatsdConfig(const StatsdConfig& config, std::set<int>& allTagIds, std::vector<sp<LogMatchingTracker>>& allLogEntryMatchers, @@ -91,8 +90,6 @@ bool initStatsdConfig(const StatsdConfig& config, std::set<int>& allTagIds, std::unordered_map<int, std::vector<int>>& trackerToMetricMap, std::unordered_map<int, std::vector<int>>& trackerToConditionMap); -int getTrackerIndex(const std::string& name, const std::unordered_map<string, int>& logTrackerMap); - } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/stats_events.proto b/cmds/statsd/src/stats_events.proto index cd00ba8e8898..39621406222e 100644 --- a/cmds/statsd/src/stats_events.proto +++ b/cmds/statsd/src/stats_events.proto @@ -36,9 +36,28 @@ option java_outer_classname = "StatsEventProto"; */ message StatsEvent { oneof event { - ScreenStateChanged screen_state_changed = 1; - ProcessStateChanged process_state_changed = 2; - WakeLockChanged wakelock_changed = 3; + // For StatsLog reasons, 1 is illegal and will not work. Must start at 2. + BleScanStateChanged ble_scan_state_changed = 2; + BleUnoptimizedScanStateChanged ble_unoptimized_scan_state_changed = 3; + BleScanResultReceived ble_scan_result_received = 4; + SensorStateChanged sensor_state_changed = 5; + GpsScanStateChanged gps_scan_state_changed = 6; // TODO: untested + SyncStateChanged sync_state_changed = 7; + ScheduledJobStateChanged scheduled_job_state_changed = 8; + ScreenBrightnessChanged screen_brightness_changed = 9; + // 10-20 are temporarily reserved for wakelocks etc. + UidWakelockStateChanged uid_wakelock_state_changed = 11; + LongPartialWakelockStateChanged long_partial_wakelock_state_changed = 12; + BatterySaverModeStateChanged battery_saver_mode_state_changed = 21; + DeviceIdleModeStateChanged device_idle_mode_state_changed = 22; + AudioStateChanged audio_state_changed = 23; + MediaCodecActivityChanged media_codec_activity_changed = 24; + CameraStateChanged camera_state_changed = 25; + FlashlightStateChanged flashlight_state_changed = 26; + UidProcessStateChanged uid_process_state_changed = 27; + ProcessLifeCycleStateChanged process_life_cycle_state_changed = 28; + ScreenStateChanged screen_state_changed = 29; + // TODO: Reorder the numbering so that the most frequent occur events occur in the first 15. } } @@ -72,7 +91,7 @@ message WorkSource { * and those UIDs will be translated in xxx to those strings. * * CONVENTIONS: - * - Events are past tense. e.g. ScreenStateChanged, not ScreenStateChange + * - Events are past tense. e.g. ScreenStateChanged, not ScreenStateChange. * - If there is a UID, it goes first. Think in an object-oriented fashion. * ***************************************************************************** */ @@ -98,33 +117,347 @@ message ScreenStateChanged { } /** - * Logs that the state of a process state, as per the activity manager has changed. + * Logs that the state of a process state, as per the activity manager, has changed. * * Logged from: * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java */ -message ProcessStateChanged { - // TODO: Use the real (mapped) process states. +message UidProcessStateChanged { optional int32 uid = 1; // TODO: should be a string tagged w/ uid annotation // The state. + // TODO: Use the real (mapped) process states. optional int32 state = 2; } /** - * Logs that the state of a wakelock has changed. + * Logs that a process started, finished, crashed, or ANRed. + * + * Logged from: + * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java + */ +message ProcessLifeCycleStateChanged { + // TODO: Use the real (mapped) process states. + optional int32 uid = 1; // TODO: should be a string tagged w/ uid annotation + + // TODO: What is this? + optional string name = 2; + + // The state. + // TODO: Use an enum. + optional int32 event = 3; +} + + + +/** + * Logs when the ble scan state changes. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +message BleScanStateChanged { + // TODO: Add attribution instead of uid. + optional int32 uid = 1; + + enum State { + OFF = 0; + ON = 1; + } + optional State state = 2; +} + +/** + * Logs when an unoptimized ble scan state changes. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +// TODO: Consider changing to tracking per-scanner-id (log from AppScanStats). +message BleUnoptimizedScanStateChanged { + // TODO: Add attribution instead of uid. + optional int32 uid = 1; + + enum State { + OFF = 0; + ON = 1; + } + optional State state = 2; +} + +/** + * Logs reporting of a ble scan finding results. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +// TODO: Consider changing to tracking per-scanner-id (log from AppScanStats). +message BleScanResultReceived { + // TODO: Add attribution instead of uid. + optional int32 uid = 1; + + // Number of ble scan results returned. + optional int32 num_of_results = 2; +} + +/** + * Logs when a sensor state changes. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +message SensorStateChanged { + // TODO: Add attribution instead of uid. + optional int32 uid = 1; + + // TODO: Is there a way to get the actual name of the sensor? + // The id (int) of the sensor. + optional int32 sensor_id = 2; + + enum State { + OFF = 0; + ON = 1; + } + optional State state = 3; +} + + +/** + * Logs when GPS state changes. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +message GpsScanStateChanged { + // TODO: Add attribution instead of uid. + optional int32 uid = 1; + + enum State { + OFF = 0; + ON = 1; + } + optional State state = 2; +} + + +/** + * Logs when a sync manager sync state changes. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +message SyncStateChanged { + // TODO: Add attribution instead of uid. + optional int32 uid = 1; + + // Name of the sync (as named in the app) + optional string name = 2; + + enum State { + OFF = 0; + ON = 1; + } + optional State state = 3; +} + +/** + * Logs when a job scheduler job state changes. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +message ScheduledJobStateChanged { + // TODO: Add attribution instead of uid. + optional int32 uid = 1; + + // Name of the job (as named in the app) + optional string name = 2; + + enum State { + OFF = 0; + ON = 1; + } + optional State state = 3; + + // TODO: Consider adding the stopReason (int) +} + +/** + * Logs when the audio state changes. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +message AudioStateChanged { + // TODO: Add attribution instead of uid. + optional int32 uid = 1; + + enum State { + OFF = 0; + ON = 1; + } + optional State state = 2; +} + +/** + * Logs when the video codec state changes. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +message MediaCodecActivityChanged { + // TODO: Add attribution instead of uid. + optional int32 uid = 1; + + enum State { + OFF = 0; + ON = 1; + } + optional State state = 2; +} + +/** + * Logs when the flashlight state changes. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +message FlashlightStateChanged { + // TODO: Add attribution instead of uid. + optional int32 uid = 1; + + enum State { + OFF = 0; + ON = 1; + } + optional State state = 2; +} + +/** + * Logs when the camera state changes. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +message CameraStateChanged { + // TODO: Add attribution instead of uid. + optional int32 uid = 1; + + enum State { + OFF = 0; + ON = 1; + } + optional State state = 2; +} + +/** + * Logs that the state of a wakelock (per app and per wakelock name) has changed. * * Logged from: * TODO */ -message WakeLockChanged { +message WakelockChanged { // TODO: Add attribution instead of uid. optional int32 uid = 1; + // Type of wakelock. + enum Type { + PARTIAL = 0; + FULL = 1; + WINDOW = 2; + } + optional int32 type = 2; + + // The wakelock tag (Called tag in the Java API, sometimes name elsewhere). + optional string tag = 3; + + enum State { + OFF = 0; + ON = 1; + } + optional State state = 4; +} + +/** + * Logs when an app is holding a wakelock, regardless of the wakelock's name. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +message UidWakelockStateChanged { + // TODO: Add attribution instead of uid. + optional int32 uid = 1; + + // Type of wakelock. + enum Type { + PARTIAL = 0; + FULL = 1; + WINDOW = 2; + } + optional int32 type = 2; + + enum State { + OFF = 0; + ON = 1; + } + optional State state = 3; +} + +/** + * Logs when a partial wakelock is considered 'long' (over 1 min). + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +message LongPartialWakelockStateChanged { + // TODO: Add attribution instead of uid? + optional int32 uid = 1; + // The wakelock tag (Called tag in the Java API, sometimes name elsewhere). optional string tag = 2; - // TODO: Use a constant instead of boolean? - optional bool state = 3; + // TODO: I have no idea what this is. + optional string history_tag = 3; + + enum State { + OFF = 0; + ON = 1; + } + optional State state = 4; +} + +/** + * Logs Battery Saver state change. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +message BatterySaverModeStateChanged { + enum State { + OFF = 0; + ON = 1; + } + optional State state = 1; } +/** + * Logs Doze mode state change. + * + * Logged from: + * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java + */ +message DeviceIdleModeStateChanged { + // TODO: Use the enum matching BatteryStats.DEVICE_IDLE_MODE_. + optional int32 state = 1; +} + +/** + * Logs screen brightness level. + * + * Logged from: + * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java + */ +message ScreenBrightnessChanged { + // Screen brightness level. Should be in [-1, 255] according to PowerManager.java. + optional int32 level = 1; +}
\ No newline at end of file diff --git a/cmds/statsd/src/stats_util.h b/cmds/statsd/src/stats_util.h index 575588b3ba72..39c1d59c88b7 100644 --- a/cmds/statsd/src/stats_util.h +++ b/cmds/statsd/src/stats_util.h @@ -30,6 +30,14 @@ namespace statsd { #define MATCHER_NOT_FOUND -2 #define NANO_SECONDS_IN_A_SECOND (1000 * 1000 * 1000) +// TODO: Remove the following constants once they are exposed in ProtOutputStream.h +const uint64_t FIELD_TYPE_SHIFT = 32; +const uint64_t TYPE_MESSAGE = 11ULL << FIELD_TYPE_SHIFT; +const uint64_t TYPE_INT64 = 3ULL << FIELD_TYPE_SHIFT; +const uint64_t TYPE_INT32 = 5ULL << FIELD_TYPE_SHIFT; +const uint64_t TYPE_FLOAT = 2ULL << FIELD_TYPE_SHIFT; +const uint64_t TYPE_STRING = 9ULL << FIELD_TYPE_SHIFT; + typedef std::string HashableDimensionKey; EventMetricData parse(log_msg msg); diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto index afb3f2b0db25..113ac62699d2 100644 --- a/cmds/statsd/src/statsd_config.proto +++ b/cmds/statsd/src/statsd_config.proto @@ -114,7 +114,7 @@ message Alert { optional int32 refractory_period_secs = 4; - optional int64 trigger_if_gt = 5; + optional int64 trigger_if_sum_gt = 5; } message EventMetric { diff --git a/config/compiled-classes-phone b/config/compiled-classes-phone index f32c0d68c851..47e148df1023 100644 --- a/config/compiled-classes-phone +++ b/config/compiled-classes-phone @@ -709,9 +709,9 @@ android.bluetooth.BluetoothHeadset$1 android.bluetooth.BluetoothHeadset$2 android.bluetooth.BluetoothHeadset$3 android.bluetooth.BluetoothHealthAppConfiguration -android.bluetooth.BluetoothInputDevice -android.bluetooth.BluetoothInputDevice$1 -android.bluetooth.BluetoothInputDevice$2 +android.bluetooth.BluetoothHidHost +android.bluetooth.BluetoothHidHost$1 +android.bluetooth.BluetoothHidHost$2 android.bluetooth.BluetoothInputStream android.bluetooth.BluetoothManager android.bluetooth.BluetoothMap @@ -754,9 +754,9 @@ android.bluetooth.IBluetoothHeadsetPhone$Stub$Proxy android.bluetooth.IBluetoothHealth android.bluetooth.IBluetoothHealth$Stub android.bluetooth.IBluetoothHealthCallback -android.bluetooth.IBluetoothInputDevice -android.bluetooth.IBluetoothInputDevice$Stub -android.bluetooth.IBluetoothInputDevice$Stub$Proxy +android.bluetooth.IBluetoothHidHost +android.bluetooth.IBluetoothHidHost$Stub +android.bluetooth.IBluetoothHidHost$Stub$Proxy android.bluetooth.IBluetoothManager android.bluetooth.IBluetoothManager$Stub android.bluetooth.IBluetoothManager$Stub$Proxy diff --git a/core/java/Android.bp b/core/java/Android.bp new file mode 100644 index 000000000000..42b0f6bad0ae --- /dev/null +++ b/core/java/Android.bp @@ -0,0 +1,4 @@ +filegroup { + name: "IKeyAttestationApplicationIdProvider.aidl", + srcs: ["android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl"], +} diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java index df87b4556ee5..33825b4b5f41 100644 --- a/core/java/android/app/slice/SliceProvider.java +++ b/core/java/android/app/slice/SliceProvider.java @@ -156,27 +156,34 @@ public abstract class SliceProvider extends ContentProvider { } private Slice handleBindSlice(Uri sliceUri) { - Slice[] output = new Slice[1]; - CountDownLatch latch = new CountDownLatch(1); - Handler mainHandler = new Handler(Looper.getMainLooper()); - mainHandler.post(() -> { - ThreadPolicy oldPolicy = StrictMode.getThreadPolicy(); - try { - StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() - .detectAll() - .penaltyDeath() - .build()); - output[0] = onBindSlice(sliceUri); - } finally { - StrictMode.setThreadPolicy(oldPolicy); + if (Looper.myLooper() == Looper.getMainLooper()) { + return onBindSliceStrict(sliceUri); + } else { + CountDownLatch latch = new CountDownLatch(1); + Slice[] output = new Slice[1]; + Handler.getMain().post(() -> { + output[0] = onBindSliceStrict(sliceUri); latch.countDown(); + }); + try { + latch.await(); + return output[0]; + } catch (InterruptedException e) { + throw new RuntimeException(e); } - }); + } + } + + private Slice onBindSliceStrict(Uri sliceUri) { + ThreadPolicy oldPolicy = StrictMode.getThreadPolicy(); try { - latch.await(); - return output[0]; - } catch (InterruptedException e) { - throw new RuntimeException(e); + StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() + .detectAll() + .penaltyDeath() + .build()); + return onBindSlice(sliceUri); + } finally { + StrictMode.setThreadPolicy(oldPolicy); } } } diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 3526e1896621..578a5b8b93e1 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -1134,8 +1134,32 @@ public final class BluetoothAdapter { } /** - * Sets the {@link BluetoothClass} Bluetooth Class of Device (CoD) of - * the local Bluetooth adapter. + * Returns the {@link BluetoothClass} Bluetooth Class of Device (CoD) of the local Bluetooth + * adapter. + * + * @return {@link BluetoothClass} Bluetooth CoD of local Bluetooth device. + * + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + public BluetoothClass getBluetoothClass() { + if (getState() != STATE_ON) return null; + try { + mServiceLock.readLock().lock(); + if (mService != null) return mService.getBluetoothClass(); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } + return null; + } + + /** + * Sets the {@link BluetoothClass} Bluetooth Class of Device (CoD) of the local Bluetooth + * adapter. + * + * <p>Note: This value persists across system reboot. * * @param bluetoothClass {@link BluetoothClass} to set the local Bluetooth adapter to. * @return true if successful, false if unsuccessful. @@ -2104,8 +2128,8 @@ public final class BluetoothAdapter { } else if (profile == BluetoothProfile.AVRCP_CONTROLLER) { BluetoothAvrcpController avrcp = new BluetoothAvrcpController(context, listener); return true; - } else if (profile == BluetoothProfile.INPUT_DEVICE) { - BluetoothInputDevice iDev = new BluetoothInputDevice(context, listener); + } else if (profile == BluetoothProfile.HID_HOST) { + BluetoothHidHost iDev = new BluetoothHidHost(context, listener); return true; } else if (profile == BluetoothProfile.PAN) { BluetoothPan pan = new BluetoothPan(context, listener); @@ -2128,8 +2152,8 @@ public final class BluetoothAdapter { } else if (profile == BluetoothProfile.MAP_CLIENT) { BluetoothMapClient mapClient = new BluetoothMapClient(context, listener); return true; - } else if (profile == BluetoothProfile.INPUT_HOST) { - BluetoothInputHost iHost = new BluetoothInputHost(context, listener); + } else if (profile == BluetoothProfile.HID_DEVICE) { + BluetoothHidDevice hidDevice = new BluetoothHidDevice(context, listener); return true; } else { return false; @@ -2167,8 +2191,8 @@ public final class BluetoothAdapter { BluetoothAvrcpController avrcp = (BluetoothAvrcpController) proxy; avrcp.close(); break; - case BluetoothProfile.INPUT_DEVICE: - BluetoothInputDevice iDev = (BluetoothInputDevice) proxy; + case BluetoothProfile.HID_HOST: + BluetoothHidHost iDev = (BluetoothHidHost) proxy; iDev.close(); break; case BluetoothProfile.PAN: @@ -2207,9 +2231,9 @@ public final class BluetoothAdapter { BluetoothMapClient mapClient = (BluetoothMapClient) proxy; mapClient.close(); break; - case BluetoothProfile.INPUT_HOST: - BluetoothInputHost iHost = (BluetoothInputHost) proxy; - iHost.close(); + case BluetoothProfile.HID_DEVICE: + BluetoothHidDevice hidDevice = (BluetoothHidDevice) proxy; + hidDevice.close(); break; } } diff --git a/core/java/android/bluetooth/BluetoothInputHost.java b/core/java/android/bluetooth/BluetoothHidDevice.java index e18d9d1be51b..179f36dab32f 100644 --- a/core/java/android/bluetooth/BluetoothInputHost.java +++ b/core/java/android/bluetooth/BluetoothHidDevice.java @@ -33,9 +33,9 @@ import java.util.List; /** * @hide */ -public final class BluetoothInputHost implements BluetoothProfile { +public final class BluetoothHidDevice implements BluetoothProfile { - private static final String TAG = BluetoothInputHost.class.getSimpleName(); + private static final String TAG = BluetoothHidDevice.class.getSimpleName(); /** * Intent used to broadcast the change in connection state of the Input @@ -57,7 +57,7 @@ public final class BluetoothInputHost implements BluetoothProfile { */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = - "android.bluetooth.inputhost.profile.action.CONNECTION_STATE_CHANGED"; + "android.bluetooth.hiddevice.profile.action.CONNECTION_STATE_CHANGED"; /** * Constants representing device subclass. @@ -113,7 +113,7 @@ public final class BluetoothInputHost implements BluetoothProfile { private ServiceListener mServiceListener; - private volatile IBluetoothInputHost mService; + private volatile IBluetoothHidDevice mService; private BluetoothAdapter mAdapter; @@ -205,23 +205,23 @@ public final class BluetoothInputHost implements BluetoothProfile { private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { Log.d(TAG, "onServiceConnected()"); - mService = IBluetoothInputHost.Stub.asInterface(service); + mService = IBluetoothHidDevice.Stub.asInterface(service); if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.INPUT_HOST, - BluetoothInputHost.this); + mServiceListener.onServiceConnected(BluetoothProfile.HID_DEVICE, + BluetoothHidDevice.this); } } public void onServiceDisconnected(ComponentName className) { Log.d(TAG, "onServiceDisconnected()"); mService = null; if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.INPUT_HOST); + mServiceListener.onServiceDisconnected(BluetoothProfile.HID_DEVICE); } } }; - BluetoothInputHost(Context context, ServiceListener listener) { - Log.v(TAG, "BluetoothInputHost"); + BluetoothHidDevice(Context context, ServiceListener listener) { + Log.v(TAG, "BluetoothHidDevice"); mContext = context; mServiceListener = listener; @@ -240,7 +240,7 @@ public final class BluetoothInputHost implements BluetoothProfile { } boolean doBind() { - Intent intent = new Intent(IBluetoothInputHost.class.getName()); + Intent intent = new Intent(IBluetoothHidDevice.class.getName()); ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); intent.setComponent(comp); if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, @@ -285,7 +285,7 @@ public final class BluetoothInputHost implements BluetoothProfile { public List<BluetoothDevice> getConnectedDevices() { Log.v(TAG, "getConnectedDevices()"); - final IBluetoothInputHost service = mService; + final IBluetoothHidDevice service = mService; if (service != null) { try { return service.getConnectedDevices(); @@ -306,7 +306,7 @@ public final class BluetoothInputHost implements BluetoothProfile { public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { Log.v(TAG, "getDevicesMatchingConnectionStates(): states=" + Arrays.toString(states)); - final IBluetoothInputHost service = mService; + final IBluetoothHidDevice service = mService; if (service != null) { try { return service.getDevicesMatchingConnectionStates(states); @@ -327,7 +327,7 @@ public final class BluetoothInputHost implements BluetoothProfile { public int getConnectionState(BluetoothDevice device) { Log.v(TAG, "getConnectionState(): device=" + device); - final IBluetoothInputHost service = mService; + final IBluetoothHidDevice service = mService; if (service != null) { try { return service.getConnectionState(device); @@ -367,7 +367,7 @@ public final class BluetoothInputHost implements BluetoothProfile { return false; } - final IBluetoothInputHost service = mService; + final IBluetoothHidDevice service = mService; if (service != null) { try { BluetoothHidDeviceAppConfiguration config = @@ -401,7 +401,7 @@ public final class BluetoothInputHost implements BluetoothProfile { boolean result = false; - final IBluetoothInputHost service = mService; + final IBluetoothHidDevice service = mService; if (service != null) { try { result = service.unregisterApp(config); @@ -426,7 +426,7 @@ public final class BluetoothInputHost implements BluetoothProfile { public boolean sendReport(BluetoothDevice device, int id, byte[] data) { boolean result = false; - final IBluetoothInputHost service = mService; + final IBluetoothHidDevice service = mService; if (service != null) { try { result = service.sendReport(device, id, data); @@ -454,7 +454,7 @@ public final class BluetoothInputHost implements BluetoothProfile { boolean result = false; - final IBluetoothInputHost service = mService; + final IBluetoothHidDevice service = mService; if (service != null) { try { result = service.replyReport(device, type, id, data); @@ -480,7 +480,7 @@ public final class BluetoothInputHost implements BluetoothProfile { boolean result = false; - final IBluetoothInputHost service = mService; + final IBluetoothHidDevice service = mService; if (service != null) { try { result = service.reportError(device, error); @@ -504,7 +504,7 @@ public final class BluetoothInputHost implements BluetoothProfile { boolean result = false; - final IBluetoothInputHost service = mService; + final IBluetoothHidDevice service = mService; if (service != null) { try { result = service.unplug(device); @@ -529,7 +529,7 @@ public final class BluetoothInputHost implements BluetoothProfile { boolean result = false; - final IBluetoothInputHost service = mService; + final IBluetoothHidDevice service = mService; if (service != null) { try { result = service.connect(device); @@ -553,7 +553,7 @@ public final class BluetoothInputHost implements BluetoothProfile { boolean result = false; - final IBluetoothInputHost service = mService; + final IBluetoothHidDevice service = mService; if (service != null) { try { result = service.disconnect(device); diff --git a/core/java/android/bluetooth/BluetoothInputDevice.java b/core/java/android/bluetooth/BluetoothHidHost.java index 32615761cf8c..8ad0f9d064fd 100644 --- a/core/java/android/bluetooth/BluetoothInputDevice.java +++ b/core/java/android/bluetooth/BluetoothHidHost.java @@ -35,16 +35,16 @@ import java.util.List; * This class provides the public APIs to control the Bluetooth Input * Device Profile. * - * <p>BluetoothInputDevice is a proxy object for controlling the Bluetooth + * <p>BluetoothHidHost is a proxy object for controlling the Bluetooth * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get - * the BluetoothInputDevice proxy object. + * the BluetoothHidHost proxy object. * * <p>Each method is protected with its appropriate permission. * * @hide */ -public final class BluetoothInputDevice implements BluetoothProfile { - private static final String TAG = "BluetoothInputDevice"; +public final class BluetoothHidHost implements BluetoothProfile { + private static final String TAG = "BluetoothHidHost"; private static final boolean DBG = true; private static final boolean VDBG = false; @@ -177,52 +177,52 @@ public final class BluetoothInputDevice implements BluetoothProfile { * @hide */ public static final String EXTRA_PROTOCOL_MODE = - "android.bluetooth.BluetoothInputDevice.extra.PROTOCOL_MODE"; + "android.bluetooth.BluetoothHidHost.extra.PROTOCOL_MODE"; /** * @hide */ public static final String EXTRA_REPORT_TYPE = - "android.bluetooth.BluetoothInputDevice.extra.REPORT_TYPE"; + "android.bluetooth.BluetoothHidHost.extra.REPORT_TYPE"; /** * @hide */ public static final String EXTRA_REPORT_ID = - "android.bluetooth.BluetoothInputDevice.extra.REPORT_ID"; + "android.bluetooth.BluetoothHidHost.extra.REPORT_ID"; /** * @hide */ public static final String EXTRA_REPORT_BUFFER_SIZE = - "android.bluetooth.BluetoothInputDevice.extra.REPORT_BUFFER_SIZE"; + "android.bluetooth.BluetoothHidHost.extra.REPORT_BUFFER_SIZE"; /** * @hide */ - public static final String EXTRA_REPORT = "android.bluetooth.BluetoothInputDevice.extra.REPORT"; + public static final String EXTRA_REPORT = "android.bluetooth.BluetoothHidHost.extra.REPORT"; /** * @hide */ - public static final String EXTRA_STATUS = "android.bluetooth.BluetoothInputDevice.extra.STATUS"; + public static final String EXTRA_STATUS = "android.bluetooth.BluetoothHidHost.extra.STATUS"; /** * @hide */ public static final String EXTRA_VIRTUAL_UNPLUG_STATUS = - "android.bluetooth.BluetoothInputDevice.extra.VIRTUAL_UNPLUG_STATUS"; + "android.bluetooth.BluetoothHidHost.extra.VIRTUAL_UNPLUG_STATUS"; /** * @hide */ public static final String EXTRA_IDLE_TIME = - "android.bluetooth.BluetoothInputDevice.extra.IDLE_TIME"; + "android.bluetooth.BluetoothHidHost.extra.IDLE_TIME"; private Context mContext; private ServiceListener mServiceListener; private BluetoothAdapter mAdapter; - private volatile IBluetoothInputDevice mService; + private volatile IBluetoothHidHost mService; private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = new IBluetoothStateChangeCallback.Stub() { @@ -254,10 +254,10 @@ public final class BluetoothInputDevice implements BluetoothProfile { }; /** - * Create a BluetoothInputDevice proxy object for interacting with the local + * Create a BluetoothHidHost proxy object for interacting with the local * Bluetooth Service which handles the InputDevice profile */ - /*package*/ BluetoothInputDevice(Context context, ServiceListener l) { + /*package*/ BluetoothHidHost(Context context, ServiceListener l) { mContext = context; mServiceListener = l; mAdapter = BluetoothAdapter.getDefaultAdapter(); @@ -275,7 +275,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { } boolean doBind() { - Intent intent = new Intent(IBluetoothInputDevice.class.getName()); + Intent intent = new Intent(IBluetoothHidHost.class.getName()); ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); intent.setComponent(comp); if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, @@ -331,7 +331,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { return service.connect(device); @@ -371,7 +371,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { return service.disconnect(device); @@ -390,7 +390,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { @Override public List<BluetoothDevice> getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled()) { try { return service.getConnectedDevices(); @@ -409,7 +409,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { @Override public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled()) { try { return service.getDevicesMatchingConnectionStates(states); @@ -428,7 +428,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { @Override public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getConnectionState(device); @@ -458,7 +458,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { @@ -490,7 +490,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getPriority(device); @@ -506,11 +506,11 @@ public final class BluetoothInputDevice implements BluetoothProfile { private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothInputDevice.Stub.asInterface(Binder.allowBlocking(service)); + mService = IBluetoothHidHost.Stub.asInterface(Binder.allowBlocking(service)); if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.INPUT_DEVICE, - BluetoothInputDevice.this); + mServiceListener.onServiceConnected(BluetoothProfile.HID_HOST, + BluetoothHidHost.this); } } @@ -518,7 +518,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { if (DBG) Log.d(TAG, "Proxy object disconnected"); mService = null; if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.INPUT_DEVICE); + mServiceListener.onServiceDisconnected(BluetoothProfile.HID_HOST); } } }; @@ -542,7 +542,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean virtualUnplug(BluetoothDevice device) { if (DBG) log("virtualUnplug(" + device + ")"); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { return service.virtualUnplug(device); @@ -568,7 +568,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean getProtocolMode(BluetoothDevice device) { if (VDBG) log("getProtocolMode(" + device + ")"); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getProtocolMode(device); @@ -592,7 +592,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean setProtocolMode(BluetoothDevice device, int protocolMode) { if (DBG) log("setProtocolMode(" + device + ")"); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { return service.setProtocolMode(device, protocolMode); @@ -623,7 +623,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { log("getReport(" + device + "), reportType=" + reportType + " reportId=" + reportId + "bufferSize=" + bufferSize); } - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getReport(device, reportType, reportId, bufferSize); @@ -649,7 +649,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean setReport(BluetoothDevice device, byte reportType, String report) { if (VDBG) log("setReport(" + device + "), reportType=" + reportType + " report=" + report); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { return service.setReport(device, reportType, report); @@ -674,7 +674,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean sendData(BluetoothDevice device, String report) { if (DBG) log("sendData(" + device + "), report=" + report); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { return service.sendData(device, report); @@ -698,7 +698,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean getIdleTime(BluetoothDevice device) { if (DBG) log("getIdletime(" + device + ")"); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getIdleTime(device); @@ -723,7 +723,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean setIdleTime(BluetoothDevice device, byte idleTime) { if (DBG) log("setIdletime(" + device + "), idleTime=" + idleTime); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { return service.setIdleTime(device, idleTime); diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java index bc8fa846087d..46a230b50605 100644 --- a/core/java/android/bluetooth/BluetoothProfile.java +++ b/core/java/android/bluetooth/BluetoothProfile.java @@ -73,11 +73,11 @@ public interface BluetoothProfile { public static final int HEALTH = 3; /** - * Input Device Profile + * HID Host * * @hide */ - public static final int INPUT_DEVICE = 4; + public static final int HID_HOST = 4; /** * PAN Profile @@ -152,11 +152,11 @@ public interface BluetoothProfile { public static final int MAP_CLIENT = 18; /** - * Input Host + * HID Device * * @hide */ - public static final int INPUT_HOST = 19; + public static final int HID_DEVICE = 19; /** * Max profile ID. This value should be updated whenever a new profile is added to match diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index d73f8526843c..dcc682116993 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -375,7 +375,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * {@code DownloadManager}, {@code MediaPlayer}) will refuse app's requests to use cleartext * traffic. Third-party libraries are encouraged to honor this flag as well. * - * <p>NOTE: {@code WebView} does not honor this flag. + * <p>NOTE: {@code WebView} honors this flag for applications targeting API level 26 and up. * * <p>This flag is ignored on Android N and above if an Android Network Security Config is * present. diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java index 143c51da5367..7d58658cb272 100644 --- a/core/java/android/content/pm/PackageManagerInternal.java +++ b/core/java/android/content/pm/PackageManagerInternal.java @@ -467,7 +467,4 @@ public abstract class PackageManagerInternal { /** Updates the flags for the given permission. */ public abstract void updatePermissionFlagsTEMP(@NonNull String permName, @NonNull String packageName, int flagMask, int flagValues, int userId); - /** Returns a PermissionGroup. */ - public abstract @Nullable PackageParser.PermissionGroup getPermissionGroupTEMP( - @NonNull String groupName); } diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java index 595d85715fc9..996824d4393d 100644 --- a/core/java/android/hardware/usb/UsbManager.java +++ b/core/java/android/hardware/usb/UsbManager.java @@ -102,7 +102,7 @@ public class UsbManager { "android.hardware.usb.action.USB_PORT_CHANGED"; /** - * Broadcast Action: A broadcast for USB device attached event. + * Activity intent sent when a USB device is attached. * * This intent is sent when a USB device is attached to the USB bus when in host mode. * <ul> @@ -128,9 +128,8 @@ public class UsbManager { "android.hardware.usb.action.USB_DEVICE_DETACHED"; /** - * Broadcast Action: A broadcast for USB accessory attached event. + * Activity intent sent when a USB accessory is attached. * - * This intent is sent when a USB accessory is attached. * <ul> * <li> {@link #EXTRA_ACCESSORY} containing the {@link android.hardware.usb.UsbAccessory} * for the attached accessory diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java index 2468225fe62f..2c7e51a1db25 100644 --- a/core/java/android/inputmethodservice/IInputMethodWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java @@ -48,6 +48,7 @@ import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; /** * Implements the internal IInputMethod interface to convert incoming calls @@ -76,6 +77,20 @@ class IInputMethodWrapper extends IInputMethod.Stub final WeakReference<InputMethod> mInputMethod; final int mTargetSdkVersion; + /** + * This is not {@null} only between {@link #bindInput(InputBinding)} and {@link #unbindInput()} + * so that {@link InputConnectionWrapper} can query if {@link #unbindInput()} has already been + * called or not, mainly to avoid unnecessary blocking operations. + * + * <p>This field must be set and cleared only from the binder thread(s), where the system + * guarantees that {@link #bindInput(InputBinding)}, + * {@link #startInput(IBinder, IInputContext, int, EditorInfo, boolean)}, and + * {@link #unbindInput()} are called with the same order as the original calls + * in {@link com.android.server.InputMethodManagerService}. See {@link IBinder#FLAG_ONEWAY} + * for detailed semantics.</p> + */ + AtomicBoolean mIsUnbindIssued = null; + // NOTE: we should have a cache of these. static final class InputMethodSessionCallbackWrapper implements InputMethod.SessionCallback { final Context mContext; @@ -163,8 +178,10 @@ class IInputMethodWrapper extends IInputMethod.Stub final IBinder startInputToken = (IBinder) args.arg1; final IInputContext inputContext = (IInputContext) args.arg2; final EditorInfo info = (EditorInfo) args.arg3; + final AtomicBoolean isUnbindIssued = (AtomicBoolean) args.arg4; final InputConnection ic = inputContext != null - ? new InputConnectionWrapper(mTarget, inputContext, missingMethods) : null; + ? new InputConnectionWrapper( + mTarget, inputContext, missingMethods, isUnbindIssued) : null; info.makeCompatible(mTargetSdkVersion); inputMethod.dispatchStartInputWithToken(ic, info, restarting /* restarting */, startInputToken); @@ -236,10 +253,15 @@ class IInputMethodWrapper extends IInputMethod.Stub @BinderThread @Override public void bindInput(InputBinding binding) { + if (mIsUnbindIssued != null) { + Log.e(TAG, "bindInput must be paired with unbindInput."); + } + mIsUnbindIssued = new AtomicBoolean(); // This IInputContext is guaranteed to implement all the methods. final int missingMethodFlags = 0; InputConnection ic = new InputConnectionWrapper(mTarget, - IInputContext.Stub.asInterface(binding.getConnectionToken()), missingMethodFlags); + IInputContext.Stub.asInterface(binding.getConnectionToken()), missingMethodFlags, + mIsUnbindIssued); InputBinding nu = new InputBinding(ic, binding); mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_INPUT_CONTEXT, nu)); } @@ -247,6 +269,13 @@ class IInputMethodWrapper extends IInputMethod.Stub @BinderThread @Override public void unbindInput() { + if (mIsUnbindIssued != null) { + // Signal the flag then forget it. + mIsUnbindIssued.set(true); + mIsUnbindIssued = null; + } else { + Log.e(TAG, "unbindInput must be paired with bindInput."); + } mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_UNSET_INPUT_CONTEXT)); } @@ -255,8 +284,13 @@ class IInputMethodWrapper extends IInputMethod.Stub public void startInput(IBinder startInputToken, IInputContext inputContext, @InputConnectionInspector.MissingMethodFlags final int missingMethods, EditorInfo attribute, boolean restarting) { - mCaller.executeOrSendMessage(mCaller.obtainMessageIIOOO(DO_START_INPUT, - missingMethods, restarting ? 1 : 0, startInputToken, inputContext, attribute)); + if (mIsUnbindIssued == null) { + Log.e(TAG, "startInput must be called after bindInput."); + mIsUnbindIssued = new AtomicBoolean(); + } + mCaller.executeOrSendMessage(mCaller.obtainMessageIIOOOO(DO_START_INPUT, + missingMethods, restarting ? 1 : 0, startInputToken, inputContext, attribute, + mIsUnbindIssued)); } @BinderThread diff --git a/core/java/android/net/metrics/ConnectStats.java b/core/java/android/net/metrics/ConnectStats.java index 30b2656227d0..2495cab1adf9 100644 --- a/core/java/android/net/metrics/ConnectStats.java +++ b/core/java/android/net/metrics/ConnectStats.java @@ -20,6 +20,7 @@ import android.net.NetworkCapabilities; import android.system.OsConstants; import android.util.IntArray; import android.util.SparseIntArray; + import com.android.internal.util.BitUtils; import com.android.internal.util.TokenBucket; @@ -43,6 +44,8 @@ public class ConnectStats { public final TokenBucket mLatencyTb; /** Maximum number of latency values recorded. */ public final int mMaxLatencyRecords; + /** Total count of events */ + public int eventCount = 0; /** Total count of successful connects. */ public int connectCount = 0; /** Total count of successful connects done in blocking mode. */ @@ -57,12 +60,15 @@ public class ConnectStats { mMaxLatencyRecords = maxLatencyRecords; } - public void addEvent(int errno, int latencyMs, String ipAddr) { + boolean addEvent(int errno, int latencyMs, String ipAddr) { + eventCount++; if (isSuccess(errno)) { countConnect(errno, ipAddr); countLatency(errno, latencyMs); + return true; } else { countError(errno); + return false; } } @@ -101,7 +107,7 @@ public class ConnectStats { return (errno == 0) || isNonBlocking(errno); } - private static boolean isNonBlocking(int errno) { + static boolean isNonBlocking(int errno) { // On non-blocking TCP sockets, connect() immediately returns EINPROGRESS. // On non-blocking TCP sockets that are connecting, connect() immediately returns EALREADY. return (errno == EINPROGRESS) || (errno == EALREADY); @@ -117,6 +123,7 @@ public class ConnectStats { for (int t : BitUtils.unpackBits(transports)) { builder.append(NetworkCapabilities.transportNameOf(t)).append(", "); } + builder.append(String.format("%d events, ", eventCount)); builder.append(String.format("%d success, ", connectCount)); builder.append(String.format("%d blocking, ", connectBlockingCount)); builder.append(String.format("%d IPv6 dst", ipv6ConnectCount)); diff --git a/core/java/android/net/metrics/DnsEvent.java b/core/java/android/net/metrics/DnsEvent.java index a4970e4d0d28..81b098bbb38e 100644 --- a/core/java/android/net/metrics/DnsEvent.java +++ b/core/java/android/net/metrics/DnsEvent.java @@ -17,11 +17,13 @@ package android.net.metrics; import android.net.NetworkCapabilities; -import java.util.Arrays; + import com.android.internal.util.BitUtils; +import java.util.Arrays; + /** - * A DNS event recorded by NetdEventListenerService. + * A batch of DNS events recorded by NetdEventListenerService for a specific network. * {@hide} */ final public class DnsEvent { @@ -38,6 +40,8 @@ final public class DnsEvent { // the eventTypes, returnCodes, and latenciesMs arrays have the same length and the i-th event // is spread across the three array at position i. public int eventCount; + // The number of successful DNS queries recorded. + public int successCount; // The types of DNS queries as defined in INetdEventListener. public byte[] eventTypes; // Current getaddrinfo codes go from 1 to EAI_MAX = 15. gethostbyname returns errno, but there @@ -54,10 +58,11 @@ final public class DnsEvent { latenciesMs = new int[initialCapacity]; } - public void addResult(byte eventType, byte returnCode, int latencyMs) { + boolean addResult(byte eventType, byte returnCode, int latencyMs) { + boolean isSuccess = (returnCode == 0); if (eventCount >= SIZE_LIMIT) { // TODO: implement better rate limiting that does not biases metrics. - return; + return isSuccess; } if (eventCount == eventTypes.length) { resize((int) (1.4 * eventCount)); @@ -66,6 +71,10 @@ final public class DnsEvent { returnCodes[eventCount] = returnCode; latenciesMs[eventCount] = latencyMs; eventCount++; + if (isSuccess) { + successCount++; + } + return isSuccess; } public void resize(int newLength) { @@ -80,6 +89,8 @@ final public class DnsEvent { for (int t : BitUtils.unpackBits(transports)) { builder.append(NetworkCapabilities.transportNameOf(t)).append(", "); } - return builder.append(eventCount).append(" events)").toString(); + builder.append(String.format("%d events, ", eventCount)); + builder.append(String.format("%d success)", successCount)); + return builder.toString(); } } diff --git a/core/java/android/net/metrics/NetworkMetrics.java b/core/java/android/net/metrics/NetworkMetrics.java new file mode 100644 index 000000000000..2b662a0c28e2 --- /dev/null +++ b/core/java/android/net/metrics/NetworkMetrics.java @@ -0,0 +1,168 @@ +/* + * 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.net.metrics; + +import android.net.NetworkCapabilities; + +import com.android.internal.util.BitUtils; +import com.android.internal.util.TokenBucket; + +import java.util.StringJoiner; + +/** + * A class accumulating network metrics received from Netd regarding dns queries and + * connect() calls on a given network. + * + * This class also accumulates running sums of dns and connect latency stats and + * error counts for bug report logging. + * + * @hide + */ +public class NetworkMetrics { + + private static final int INITIAL_DNS_BATCH_SIZE = 100; + private static final int CONNECT_LATENCY_MAXIMUM_RECORDS = 20000; + + // The network id of the Android Network. + public final int netId; + // The transport types bitmap of the Android Network, as defined in NetworkCapabilities.java. + public final long transports; + // Accumulated metrics for connect events. + public final ConnectStats connectMetrics; + // Accumulated metrics for dns events. + public final DnsEvent dnsMetrics; + // Running sums of latencies and error counts for connect and dns events. + public final Summary summary; + // Running sums of the most recent latencies and error counts for connect and dns events. + // Starts null until some events are accumulated. + // Allows to collect periodic snapshot of the running summaries for a given network. + public Summary pendingSummary; + + public NetworkMetrics(int netId, long transports, TokenBucket tb) { + this.netId = netId; + this.transports = transports; + this.connectMetrics = + new ConnectStats(netId, transports, tb, CONNECT_LATENCY_MAXIMUM_RECORDS); + this.dnsMetrics = new DnsEvent(netId, transports, INITIAL_DNS_BATCH_SIZE); + this.summary = new Summary(netId, transports); + } + + /** + * Get currently pending Summary statistics, if any, for this NetworkMetrics, merge them + * into the long running Summary statistics of this NetworkMetrics, and also clear them. + */ + public Summary getPendingStats() { + Summary s = pendingSummary; + pendingSummary = null; + if (s != null) { + summary.merge(s); + } + return s; + } + + /** Accumulate a dns query result reported by netd. */ + public void addDnsResult(int eventType, int returnCode, int latencyMs) { + if (pendingSummary == null) { + pendingSummary = new Summary(netId, transports); + } + boolean isSuccess = dnsMetrics.addResult((byte) eventType, (byte) returnCode, latencyMs); + pendingSummary.dnsLatencies.count(latencyMs); + pendingSummary.dnsErrorRate.count(isSuccess ? 0 : 1); + } + + /** Accumulate a connect query result reported by netd. */ + public void addConnectResult(int error, int latencyMs, String ipAddr) { + if (pendingSummary == null) { + pendingSummary = new Summary(netId, transports); + } + boolean isSuccess = connectMetrics.addEvent(error, latencyMs, ipAddr); + pendingSummary.connectErrorRate.count(isSuccess ? 0 : 1); + if (ConnectStats.isNonBlocking(error)) { + pendingSummary.connectLatencies.count(latencyMs); + } + } + + /** Represents running sums for dns and connect average error counts and average latencies. */ + public static class Summary { + + public final int netId; + public final long transports; + // DNS latencies measured in milliseconds. + public final Metrics dnsLatencies = new Metrics(); + // DNS error rate measured in percentage points. + public final Metrics dnsErrorRate = new Metrics(); + // Blocking connect latencies measured in milliseconds. + public final Metrics connectLatencies = new Metrics(); + // Blocking and non blocking connect error rate measured in percentage points. + public final Metrics connectErrorRate = new Metrics(); + + public Summary(int netId, long transports) { + this.netId = netId; + this.transports = transports; + } + + void merge(Summary that) { + dnsLatencies.merge(that.dnsLatencies); + dnsErrorRate.merge(that.dnsErrorRate); + connectLatencies.merge(that.connectLatencies); + connectErrorRate.merge(that.connectErrorRate); + } + + @Override + public String toString() { + StringJoiner j = new StringJoiner(", ", "{", "}"); + j.add("netId=" + netId); + for (int t : BitUtils.unpackBits(transports)) { + j.add(NetworkCapabilities.transportNameOf(t)); + } + j.add(String.format("dns avg=%dms max=%dms err=%.1f%% tot=%d", + (int) dnsLatencies.average(), (int) dnsLatencies.max, + 100 * dnsErrorRate.average(), dnsErrorRate.count)); + j.add(String.format("connect avg=%dms max=%dms err=%.1f%% tot=%d", + (int) connectLatencies.average(), (int) connectLatencies.max, + 100 * connectErrorRate.average(), connectErrorRate.count)); + return j.toString(); + } + } + + /** Tracks a running sum and returns the average of a metric. */ + static class Metrics { + public double sum; + public double max = Double.MIN_VALUE; + public int count; + + void merge(Metrics that) { + this.count += that.count; + this.sum += that.sum; + this.max = Math.max(this.max, that.max); + } + + void count(double value) { + count++; + sum += value; + max = Math.max(max, value); + } + + double average() { + double a = sum / (double) count; + if (Double.isNaN(a)) { + a = 0; + } + return a; + } + } +} diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java index 270e63f408a7..c1c65ffcc049 100644 --- a/core/java/android/os/HwBinder.java +++ b/core/java/android/os/HwBinder.java @@ -48,7 +48,8 @@ public abstract class HwBinder implements IHwBinder { public static native final IHwBinder getService( String iface, - String serviceName) + String serviceName, + boolean isTry) throws RemoteException, NoSuchElementException; public static native final void configureRpcThreadpool( diff --git a/core/java/android/os/IStatsCallbacks.aidl b/core/java/android/os/IStatsCallbacks.aidl new file mode 100644 index 000000000000..02e7cd3978c8 --- /dev/null +++ b/core/java/android/os/IStatsCallbacks.aidl @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2016, 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.os; + +/** + * Callback for Statsd to allow binder calls to clients. + * {@hide} + */ +interface IStatsCallbacks { + void onReceiveLogs(out byte[] log); +} diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl index daacc4e832f9..480296c12e50 100644 --- a/core/java/android/os/IStatsManager.aidl +++ b/core/java/android/os/IStatsManager.aidl @@ -16,6 +16,8 @@ package android.os; +import android.os.IStatsCallbacks; + /** * Binder interface to communicate with the statistics management service. * {@hide} @@ -61,4 +63,15 @@ interface IStatsManager { * Inform stats that an app was removed. */ oneway void informOnePackageRemoved(in String app, in int uid); + + /** + * Trigger pushLog to force push stats log entries from statsd on client side. + */ + void requestPush(); + + /** + * Listen to statsd to send stats log entries. + * TODO: Limit callbacks with specific configurations. + */ + void subscribeStatsLog(IStatsCallbacks callbacks); } diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java index 826ec1eb129e..f52d94e4e812 100644 --- a/core/java/android/os/StrictMode.java +++ b/core/java/android/os/StrictMode.java @@ -20,7 +20,6 @@ import android.annotation.Nullable; import android.annotation.TestApi; import android.app.ActivityManager; import android.app.ActivityThread; -import android.app.ApplicationErrorReport; import android.app.IActivityManager; import android.content.BroadcastReceiver; import android.content.Context; @@ -48,8 +47,10 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.net.InetAddress; import java.net.UnknownHostException; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; +import java.util.Deque; import java.util.HashMap; import java.util.concurrent.atomic.AtomicInteger; @@ -352,8 +353,8 @@ public final class StrictMode { } else { msg = "StrictMode policy violation:"; } - if (info.crashInfo != null) { - Log.d(TAG, msg + " " + info.crashInfo.stackTrace); + if (info.hasStackTrace()) { + Log.d(TAG, msg + " " + info.getStackTrace()); } else { Log.d(TAG, msg + " missing stack trace!"); } @@ -1247,28 +1248,6 @@ public final class StrictMode { } } - /** Like parsePolicyFromMessage(), but returns the violation. */ - private static int parseViolationFromMessage(String message) { - if (message == null) { - return 0; - } - int violationIndex = message.indexOf("violation="); - if (violationIndex == -1) { - return 0; - } - int numberStartIndex = violationIndex + "violation=".length(); - int numberEndIndex = message.indexOf(' ', numberStartIndex); - if (numberEndIndex == -1) { - numberEndIndex = message.length(); - } - String violationString = message.substring(numberStartIndex, numberEndIndex); - try { - return Integer.parseInt(violationString); - } catch (NumberFormatException e) { - return 0; - } - } - private static final ThreadLocal<ArrayList<ViolationInfo>> violationsBeingTimed = new ThreadLocal<ArrayList<ViolationInfo>>() { @Override @@ -1516,7 +1495,7 @@ public final class StrictMode { // to people who push/pop temporary policy in regions of code, // hence the policy being passed around. void handleViolation(final ViolationInfo info) { - if (info == null || info.crashInfo == null || info.crashInfo.stackTrace == null) { + if (info == null || !info.hasStackTrace()) { Log.wtf(TAG, "unexpected null stacktrace"); return; } @@ -1530,7 +1509,7 @@ public final class StrictMode { gatheredViolations.set(violations); } for (ViolationInfo previous : violations) { - if (info.crashInfo.stackTrace.equals(previous.crashInfo.stackTrace)) { + if (info.getStackTrace().equals(previous.getStackTrace())) { // Duplicate. Don't log. return; } @@ -1576,8 +1555,7 @@ public final class StrictMode { } if (violationMaskSubset != 0) { - int violationBit = parseViolationFromMessage(info.crashInfo.exceptionMessage); - violationMaskSubset |= violationBit; + violationMaskSubset |= info.getViolationBit(); final int savedPolicyMask = getThreadPolicyMask(); final boolean justDropBox = (info.policy & THREAD_PENALTY_MASK) == PENALTY_DROPBOX; @@ -1622,8 +1600,7 @@ public final class StrictMode { } private static void executeDeathPenalty(ViolationInfo info) { - int violationBit = parseViolationFromMessage(info.crashInfo.exceptionMessage); - throw new StrictModeViolation(info.policy, violationBit, null); + throw new StrictModeViolation(info.policy, info.getViolationBit(), null); } /** @@ -2027,21 +2004,14 @@ public final class StrictMode { * read back all the encoded violations. */ /* package */ static void readAndHandleBinderCallViolations(Parcel p) { - // Our own stack trace to append - StringWriter sw = new StringWriter(); - sw.append("# via Binder call with stack:\n"); - PrintWriter pw = new FastPrintWriter(sw, false, 256); - new LogStackTrace().printStackTrace(pw); - pw.flush(); - String ourStack = sw.toString(); - + LogStackTrace localCallSite = new LogStackTrace(); final int policyMask = getThreadPolicyMask(); final boolean currentlyGathering = (policyMask & PENALTY_GATHER) != 0; final int size = p.readInt(); for (int i = 0; i < size; i++) { final ViolationInfo info = new ViolationInfo(p, !currentlyGathering); - info.crashInfo.appendStackTrace(ourStack); + info.addLocalStack(localCallSite); BlockGuard.Policy policy = BlockGuard.getThreadPolicy(); if (policy instanceof AndroidBlockGuardPolicy) { ((AndroidBlockGuardPolicy) policy).handleViolationWithTimingAttempt(info); @@ -2366,10 +2336,18 @@ public final class StrictMode { */ @TestApi public static final class ViolationInfo implements Parcelable { - public final String message; + /** Some VM violations provide additional information outside the throwable. */ + @Nullable private final String mMessagePrefix; - /** Stack and other stuff info. */ - @Nullable public final ApplicationErrorReport.CrashInfo crashInfo; + /** Stack and violation details. */ + @Nullable private final Throwable mThrowable; + + private final Deque<Throwable> mBinderStack = new ArrayDeque<>(); + + /** Memoized stack trace of full violation. */ + @Nullable private String mStackTrace; + /** Memoized violation bit. */ + private int mViolationBit; /** The strict mode policy mask at the time of violation. */ public final int policy; @@ -2404,19 +2382,25 @@ public final class StrictMode { /** Create an uninitialized instance of ViolationInfo */ public ViolationInfo() { - message = null; - crashInfo = null; + mMessagePrefix = null; + mThrowable = null; policy = 0; } + /** Create an instance of ViolationInfo. */ public ViolationInfo(Throwable tr, int policy) { this(null, tr, policy); } - /** Create an instance of ViolationInfo initialized from an exception. */ - public ViolationInfo(String message, Throwable tr, int policy) { - this.message = message; - crashInfo = new ApplicationErrorReport.CrashInfo(tr); + /** + * Create an instance of ViolationInfo initialized from an exception with a message prefix. + * + * @deprecated prefixes belong in the Throwable. + */ + @Deprecated + public ViolationInfo(String messagePrefix, Throwable tr, int policy) { + this.mMessagePrefix = messagePrefix; + this.mThrowable = tr; violationUptimeMillis = SystemClock.uptimeMillis(); this.policy = policy; this.numAnimationsRunning = ValueAnimator.getCurrentAnimationsCount(); @@ -2446,11 +2430,101 @@ public final class StrictMode { } } + /** Equivalent output to {@link ApplicationErrorReport.CrashInfo#stackTrace}. */ + public String getStackTrace() { + if (mThrowable != null && mStackTrace == null) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new FastPrintWriter(sw, false, 256); + mThrowable.printStackTrace(pw); + for (Throwable t : mBinderStack) { + pw.append("# via Binder call with stack:\n"); + t.printStackTrace(pw); + } + pw.flush(); + pw.close(); + mStackTrace = sw.toString(); + } + return mStackTrace; + } + + /** + * Optional message describing this violation. + * + * @hide + */ + @TestApi + public String getViolationDetails() { + if (mThrowable != null) { + return mThrowable.getMessage(); + } else { + return ""; + } + } + + /** + * A handful of VM violations provide extra information that should be presented before + * {@link #getViolationDetails()}. + * + * @hide + */ + @TestApi + public String getMessagePrefix() { + return mMessagePrefix != null ? mMessagePrefix : ""; + } + + /** If this violation has a useful stack trace. + * + * @hide + */ + public boolean hasStackTrace() { + return mThrowable != null; + } + + /** + * Add a {@link Throwable} from the current process that caused the underlying violation. + * + * @hide + */ + void addLocalStack(Throwable t) { + mBinderStack.addFirst(t); + } + + /** + * Retrieve the type of StrictMode violation. + * + * @hide + */ + int getViolationBit() { + if (mThrowable == null || mThrowable.getMessage() == null) { + return 0; + } + if (mViolationBit != 0) { + return mViolationBit; + } + String message = mThrowable.getMessage(); + int violationIndex = message.indexOf("violation="); + if (violationIndex == -1) { + return 0; + } + int numberStartIndex = violationIndex + "violation=".length(); + int numberEndIndex = message.indexOf(' ', numberStartIndex); + if (numberEndIndex == -1) { + numberEndIndex = message.length(); + } + String violationString = message.substring(numberStartIndex, numberEndIndex); + try { + mViolationBit = Integer.parseInt(violationString); + return mViolationBit; + } catch (NumberFormatException e) { + return 0; + } + } + @Override public int hashCode() { int result = 17; - if (crashInfo != null) { - result = 37 * result + crashInfo.stackTrace.hashCode(); + if (mThrowable != null) { + result = 37 * result + mThrowable.hashCode(); } if (numAnimationsRunning != 0) { result *= 37; @@ -2478,11 +2552,11 @@ public final class StrictMode { * should be removed. */ public ViolationInfo(Parcel in, boolean unsetGatheringBit) { - message = in.readString(); - if (in.readInt() != 0) { - crashInfo = new ApplicationErrorReport.CrashInfo(in); - } else { - crashInfo = null; + mMessagePrefix = in.readString(); + mThrowable = (Throwable) in.readSerializable(); + int binderStackSize = in.readInt(); + for (int i = 0; i < binderStackSize; i++) { + mBinderStack.add((Throwable) in.readSerializable()); } int rawPolicy = in.readInt(); if (unsetGatheringBit) { @@ -2502,12 +2576,11 @@ public final class StrictMode { /** Save a ViolationInfo instance to a parcel. */ @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeString(message); - if (crashInfo != null) { - dest.writeInt(1); - crashInfo.writeToParcel(dest, flags); - } else { - dest.writeInt(0); + dest.writeString(mMessagePrefix); + dest.writeSerializable(mThrowable); + dest.writeInt(mBinderStack.size()); + for (Throwable t : mBinderStack) { + dest.writeSerializable(t); } int start = dest.dataPosition(); dest.writeInt(policy); @@ -2542,8 +2615,8 @@ public final class StrictMode { /** Dump a ViolationInfo instance to a Printer. */ public void dump(Printer pw, String prefix) { - if (crashInfo != null) { - crashInfo.dump(pw, prefix); + if (mThrowable != null) { + pw.println(prefix + "stackTrace: " + getStackTrace()); } pw.println(prefix + "policy: " + policy); if (durationMillis != -1) { diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index c200ae742125..a27df3a70259 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -7615,6 +7615,13 @@ public final class Settings { public static final String AIRPLANE_MODE_TOGGLEABLE_RADIOS = "airplane_mode_toggleable_radios"; /** + * An integer representing the Bluetooth Class of Device (CoD). + * + * @hide + */ + public static final String BLUETOOTH_CLASS_OF_DEVICE = "bluetooth_class_of_device"; + + /** * A Long representing a bitmap of profiles that should be disabled when bluetooth starts. * See {@link android.bluetooth.BluetoothProfile}. * {@hide} @@ -9649,7 +9656,7 @@ public final class Settings { * Get the key that retrieves a bluetooth Input Device's priority. * @hide */ - public static final String getBluetoothInputDevicePriorityKey(String address) { + public static final String getBluetoothHidHostPriorityKey(String address) { return BLUETOOTH_INPUT_DEVICE_PRIORITY_PREFIX + address.toUpperCase(Locale.ROOT); } diff --git a/core/java/android/security/NetworkSecurityPolicy.java b/core/java/android/security/NetworkSecurityPolicy.java index 812c956f4c61..0c4eedab2fc0 100644 --- a/core/java/android/security/NetworkSecurityPolicy.java +++ b/core/java/android/security/NetworkSecurityPolicy.java @@ -16,7 +16,6 @@ package android.security; -import android.annotation.TestApi; import android.content.Context; import android.content.pm.PackageManager; import android.security.net.config.ApplicationConfig; @@ -63,7 +62,8 @@ public class NetworkSecurityPolicy { * traffic from applications is handled by higher-level network stacks/components which can * honor this aspect of the policy. * - * <p>NOTE: {@link android.webkit.WebView} does not honor this flag. + * <p>NOTE: {@link android.webkit.WebView} honors this flag for applications targeting API level + * 26 and up. */ public boolean isCleartextTrafficPermitted() { return libcore.net.NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted(); diff --git a/core/java/android/security/net/config/NetworkSecurityConfig.java b/core/java/android/security/net/config/NetworkSecurityConfig.java index b9e550540217..52f48ef8499b 100644 --- a/core/java/android/security/net/config/NetworkSecurityConfig.java +++ b/core/java/android/security/net/config/NetworkSecurityConfig.java @@ -164,7 +164,8 @@ public final class NetworkSecurityConfig { * <p> * The default configuration has the following properties: * <ol> - * <li>Cleartext traffic is permitted for non-ephemeral apps.</li> + * <li>If the application targets API level 27 (Android O MR1) or lower then cleartext traffic + * is allowed by default.</li> * <li>Cleartext traffic is not permitted for ephemeral apps.</li> * <li>HSTS is not enforced.</li> * <li>No certificate pinning is used.</li> @@ -183,7 +184,8 @@ public final class NetworkSecurityConfig { // System certificate store, does not bypass static pins. .addCertificatesEntryRef( new CertificatesEntryRef(SystemCertificateSource.getInstance(), false)); - final boolean cleartextTrafficPermitted = info.targetSandboxVersion < 2; + final boolean cleartextTrafficPermitted = info.targetSdkVersion < Build.VERSION_CODES.P + && info.targetSandboxVersion < 2; builder.setCleartextTrafficPermitted(cleartextTrafficPermitted); // Applications targeting N and above must opt in into trusting the user added certificate // store. diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index ac5c2e926874..4d2a9629c83a 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -1910,7 +1910,7 @@ public abstract class Layout { MeasuredText mt = MeasuredText.obtain(); TextLine tl = TextLine.obtain(); try { - mt.setPara(text, start, end, textDir, null); + mt.setPara(text, start, end, textDir); Directions directions; int dir; if (mt.mEasy) { diff --git a/core/java/android/text/MeasuredText.java b/core/java/android/text/MeasuredText.java index ffc44a72c357..3d9fba71db74 100644 --- a/core/java/android/text/MeasuredText.java +++ b/core/java/android/text/MeasuredText.java @@ -39,7 +39,6 @@ class MeasuredText { private int mPos; private TextPaint mWorkPaint; - private StaticLayout.Builder mBuilder; private MeasuredText() { mWorkPaint = new TextPaint(); @@ -82,7 +81,6 @@ class MeasuredText { void finish() { mText = null; - mBuilder = null; if (mLen > 1000) { mWidths = null; mChars = null; @@ -93,9 +91,7 @@ class MeasuredText { /** * Analyzes text for bidirectional runs. Allocates working buffers. */ - void setPara(CharSequence text, int start, int end, TextDirectionHeuristic textDir, - StaticLayout.Builder builder) { - mBuilder = builder; + void setPara(CharSequence text, int start, int end, TextDirectionHeuristic textDir) { mText = text; mTextStart = start; @@ -159,12 +155,12 @@ class MeasuredText { /** * Apply the style. * - * If StaticLyaout.Builder is not provided in setPara() method, this method measures the styled - * text width. - * If StaticLayout.Builder is provided in setPara() method, this method just passes the style - * information to native code by calling StaticLayout.Builder.addstyleRun() and returns 0. + * If nativeStaticLayoutPtr is 0, this method measures the styled text width. + * If nativeStaticLayoutPtr is not 0, this method just passes the style information to native + * code by calling StaticLayout.addstyleRun() and returns 0. */ - float addStyleRun(TextPaint paint, int len, Paint.FontMetricsInt fm) { + float addStyleRun(TextPaint paint, int len, Paint.FontMetricsInt fm, + long nativeStaticLayoutPtr) { if (fm != null) { paint.getFontMetricsInt(fm); } @@ -174,10 +170,10 @@ class MeasuredText { if (mEasy) { final boolean isRtl = mDir != Layout.DIR_LEFT_TO_RIGHT; - if (mBuilder == null) { + if (nativeStaticLayoutPtr == 0) { return paint.getTextRunAdvances(mChars, p, len, p, len, isRtl, mWidths, p); } else { - mBuilder.addStyleRun(paint, p, p + len, isRtl); + StaticLayout.addStyleRun(nativeStaticLayoutPtr, paint, p, p + len, isRtl); return 0.0f; // Builder.addStyleRun doesn't return the width. } } @@ -187,12 +183,12 @@ class MeasuredText { for (int q = p, i = p + 1, e = p + len;; ++i) { if (i == e || mLevels[i] != level) { final boolean isRtl = (level & 0x1) != 0; - if (mBuilder == null) { + if (nativeStaticLayoutPtr == 0) { totalAdvance += paint.getTextRunAdvances(mChars, q, i - q, q, i - q, isRtl, mWidths, q); } else { // Builder.addStyleRun doesn't return the width. - mBuilder.addStyleRun(paint, q, i, isRtl); + StaticLayout.addStyleRun(nativeStaticLayoutPtr, paint, q, i, isRtl); } if (i == e) { break; @@ -201,11 +197,15 @@ class MeasuredText { level = mLevels[i]; } } - return totalAdvance; // If mBuilder is null, the result is zero. + return totalAdvance; // If nativeStaticLayoutPtr is 0, the result is zero. + } + + float addStyleRun(TextPaint paint, int len, Paint.FontMetricsInt fm) { + return addStyleRun(paint, len, fm, 0 /* native ptr */); } float addStyleRun(TextPaint paint, MetricAffectingSpan[] spans, int len, - Paint.FontMetricsInt fm) { + Paint.FontMetricsInt fm, long nativeStaticLayoutPtr) { TextPaint workPaint = mWorkPaint; workPaint.set(paint); @@ -224,18 +224,18 @@ class MeasuredText { float wid; if (replacement == null) { - wid = addStyleRun(workPaint, len, fm); + wid = addStyleRun(workPaint, len, fm, nativeStaticLayoutPtr); } else { // Use original text. Shouldn't matter. wid = replacement.getSize(workPaint, mText, mTextStart + mPos, mTextStart + mPos + len, fm); - if (mBuilder == null) { + if (nativeStaticLayoutPtr == 0) { float[] w = mWidths; w[mPos] = wid; for (int i = mPos + 1, e = mPos + len; i < e; i++) w[i] = 0; } else { - mBuilder.addReplacementRun(paint, mPos, mPos + len, wid); + StaticLayout.addReplacementRun(nativeStaticLayoutPtr, paint, mPos, mPos + len, wid); } mPos += len; } @@ -253,6 +253,11 @@ class MeasuredText { return wid; } + float addStyleRun(TextPaint paint, MetricAffectingSpan[] spans, int len, + Paint.FontMetricsInt fm) { + return addStyleRun(paint, spans, len, fm, 0 /* native ptr */); + } + int breakText(int limit, boolean forwards, float width) { float[] w = mWidths; if (forwards) { diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index 5c60188db1e4..c0fc44fd8ee1 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -32,6 +32,9 @@ import android.util.Pools.SynchronizedPool; import com.android.internal.util.ArrayUtils; import com.android.internal.util.GrowingArrayUtils; +import dalvik.annotation.optimization.CriticalNative; +import dalvik.annotation.optimization.FastNative; + import java.util.Arrays; /** @@ -57,9 +60,7 @@ public class StaticLayout extends Layout { * default values. */ public final static class Builder { - private Builder() { - mNativePtr = nNewBuilder(); - } + private Builder() {} /** * Obtain a builder for constructing StaticLayout objects. @@ -116,13 +117,11 @@ public class StaticLayout extends Layout { b.mRightIndents = null; b.mLeftPaddings = null; b.mRightPaddings = null; - nFinishBuilder(b.mNativePtr); sPool.release(b); } // release any expensive state /* package */ void finish() { - nFinishBuilder(mNativePtr); mText = null; mPaint = null; mLeftIndents = null; @@ -405,32 +404,6 @@ public class StaticLayout extends Layout { } /** - * Measurement and break iteration is done in native code. The protocol for using - * the native code is as follows. - * - * For each paragraph, do a nSetupParagraph, which sets paragraph text, line width, tab - * stops, break strategy, and hyphenation frequency (and possibly other parameters in the - * future). - * - * Then, for each run within the paragraph: - * - one of the following, depending on the type of run: - * + addStyleRun (a text run, to be measured in native code) - * + addReplacementRun (a replacement run, width is given) - * - * Run nComputeLineBreaks() to obtain line breaks for the paragraph. - * - * After all paragraphs, call finish() to release expensive buffers. - */ - - /* package */ void addStyleRun(TextPaint paint, int start, int end, boolean isRtl) { - nAddStyleRun(mNativePtr, paint.getNativeInstance(), start, end, isRtl); - } - - /* package */ void addReplacementRun(TextPaint paint, int start, int end, float width) { - nAddReplacementRun(mNativePtr, paint.getNativeInstance(), start, end, width); - } - - /** * Build the {@link StaticLayout} after options have been set. * * <p>Note: the builder object must not be reused in any way after calling this @@ -446,17 +419,6 @@ public class StaticLayout extends Layout { return result; } - @Override - protected void finalize() throws Throwable { - try { - nFreeBuilder(mNativePtr); - } finally { - super.finalize(); - } - } - - /* package */ long mNativePtr; - private CharSequence mText; private int mStart; private int mEnd; @@ -694,270 +656,294 @@ public class StaticLayout extends Layout { indents = null; } - int paraEnd; - for (int paraStart = bufStart; paraStart <= bufEnd; paraStart = paraEnd) { - paraEnd = TextUtils.indexOf(source, CHAR_NEW_LINE, paraStart, bufEnd); - if (paraEnd < 0) - paraEnd = bufEnd; - else - paraEnd++; - - int firstWidthLineCount = 1; - int firstWidth = outerWidth; - int restWidth = outerWidth; - - LineHeightSpan[] chooseHt = null; - - if (spanned != null) { - LeadingMarginSpan[] sp = getParagraphSpans(spanned, paraStart, paraEnd, - LeadingMarginSpan.class); - for (int i = 0; i < sp.length; i++) { - LeadingMarginSpan lms = sp[i]; - firstWidth -= sp[i].getLeadingMargin(true); - restWidth -= sp[i].getLeadingMargin(false); - - // LeadingMarginSpan2 is odd. The count affects all - // leading margin spans, not just this particular one - if (lms instanceof LeadingMarginSpan2) { - LeadingMarginSpan2 lms2 = (LeadingMarginSpan2) lms; - firstWidthLineCount = Math.max(firstWidthLineCount, - lms2.getLeadingMarginLineCount()); - } + final long nativePtr = nInit( + b.mBreakStrategy, b.mHyphenationFrequency, + // TODO: Support more justification mode, e.g. letter spacing, stretching. + b.mJustificationMode != Layout.JUSTIFICATION_MODE_NONE, + indents, mLeftPaddings, mRightPaddings); + + try { + int paraEnd; + for (int paraStart = bufStart; paraStart <= bufEnd; paraStart = paraEnd) { + paraEnd = TextUtils.indexOf(source, CHAR_NEW_LINE, paraStart, bufEnd); + if (paraEnd < 0) { + paraEnd = bufEnd; + } else { + paraEnd++; } - chooseHt = getParagraphSpans(spanned, paraStart, paraEnd, LineHeightSpan.class); - - if (chooseHt.length == 0) { - chooseHt = null; // So that out() would not assume it has any contents - } else { - if (chooseHtv == null || - chooseHtv.length < chooseHt.length) { - chooseHtv = ArrayUtils.newUnpaddedIntArray(chooseHt.length); + int firstWidthLineCount = 1; + int firstWidth = outerWidth; + int restWidth = outerWidth; + + LineHeightSpan[] chooseHt = null; + + if (spanned != null) { + LeadingMarginSpan[] sp = getParagraphSpans(spanned, paraStart, paraEnd, + LeadingMarginSpan.class); + for (int i = 0; i < sp.length; i++) { + LeadingMarginSpan lms = sp[i]; + firstWidth -= sp[i].getLeadingMargin(true); + restWidth -= sp[i].getLeadingMargin(false); + + // LeadingMarginSpan2 is odd. The count affects all + // leading margin spans, not just this particular one + if (lms instanceof LeadingMarginSpan2) { + LeadingMarginSpan2 lms2 = (LeadingMarginSpan2) lms; + firstWidthLineCount = Math.max(firstWidthLineCount, + lms2.getLeadingMarginLineCount()); + } } - for (int i = 0; i < chooseHt.length; i++) { - int o = spanned.getSpanStart(chooseHt[i]); + chooseHt = getParagraphSpans(spanned, paraStart, paraEnd, LineHeightSpan.class); - if (o < paraStart) { - // starts in this layout, before the - // current paragraph + if (chooseHt.length == 0) { + chooseHt = null; // So that out() would not assume it has any contents + } else { + if (chooseHtv == null || chooseHtv.length < chooseHt.length) { + chooseHtv = ArrayUtils.newUnpaddedIntArray(chooseHt.length); + } - chooseHtv[i] = getLineTop(getLineForOffset(o)); - } else { - // starts in this paragraph + for (int i = 0; i < chooseHt.length; i++) { + int o = spanned.getSpanStart(chooseHt[i]); + + if (o < paraStart) { + // starts in this layout, before the + // current paragraph - chooseHtv[i] = v; + chooseHtv[i] = getLineTop(getLineForOffset(o)); + } else { + // starts in this paragraph + + chooseHtv[i] = v; + } } } } - } - measured.setPara(source, paraStart, paraEnd, textDir, b); - char[] chs = measured.mChars; - float[] widths = measured.mWidths; - byte[] chdirs = measured.mLevels; - int dir = measured.mDir; - boolean easy = measured.mEasy; - - // tab stop locations - int[] variableTabStops = null; - if (spanned != null) { - TabStopSpan[] spans = getParagraphSpans(spanned, paraStart, - paraEnd, TabStopSpan.class); - if (spans.length > 0) { - int[] stops = new int[spans.length]; - for (int i = 0; i < spans.length; i++) { - stops[i] = spans[i].getTabStop(); + measured.setPara(source, paraStart, paraEnd, textDir); + char[] chs = measured.mChars; + float[] widths = measured.mWidths; + byte[] chdirs = measured.mLevels; + int dir = measured.mDir; + boolean easy = measured.mEasy; + + // tab stop locations + int[] variableTabStops = null; + if (spanned != null) { + TabStopSpan[] spans = getParagraphSpans(spanned, paraStart, + paraEnd, TabStopSpan.class); + if (spans.length > 0) { + int[] stops = new int[spans.length]; + for (int i = 0; i < spans.length; i++) { + stops[i] = spans[i].getTabStop(); + } + Arrays.sort(stops, 0, stops.length); + variableTabStops = stops; } - Arrays.sort(stops, 0, stops.length); - variableTabStops = stops; - } - } - - nSetupParagraph(b.mNativePtr, chs, paraEnd - paraStart, - firstWidth, firstWidthLineCount, restWidth, - variableTabStops, TAB_INCREMENT, b.mBreakStrategy, b.mHyphenationFrequency, - // TODO: Support more justification mode, e.g. letter spacing, stretching. - b.mJustificationMode != Layout.JUSTIFICATION_MODE_NONE, - // TODO: indents and paddings don't need to get passed to native code for every - // paragraph. Pass them to native code just once. - indents, mLeftPaddings, mRightPaddings, mLineCount); - - // measurement has to be done before performing line breaking - // but we don't want to recompute fontmetrics or span ranges the - // second time, so we cache those and then use those stored values - int fmCacheCount = 0; - int spanEndCacheCount = 0; - for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) { - if (fmCacheCount * 4 >= fmCache.length) { - int[] grow = new int[fmCacheCount * 4 * 2]; - System.arraycopy(fmCache, 0, grow, 0, fmCacheCount * 4); - fmCache = grow; - } - - if (spanEndCacheCount >= spanEndCache.length) { - int[] grow = new int[spanEndCacheCount * 2]; - System.arraycopy(spanEndCache, 0, grow, 0, spanEndCacheCount); - spanEndCache = grow; - } - - if (spanned == null) { - spanEnd = paraEnd; - int spanLen = spanEnd - spanStart; - measured.addStyleRun(paint, spanLen, fm); - } else { - spanEnd = spanned.nextSpanTransition(spanStart, paraEnd, - MetricAffectingSpan.class); - int spanLen = spanEnd - spanStart; - MetricAffectingSpan[] spans = - spanned.getSpans(spanStart, spanEnd, MetricAffectingSpan.class); - spans = TextUtils.removeEmptySpans(spans, spanned, MetricAffectingSpan.class); - measured.addStyleRun(paint, spans, spanLen, fm); } - // the order of storage here (top, bottom, ascent, descent) has to match the code below - // where these values are retrieved - fmCache[fmCacheCount * 4 + 0] = fm.top; - fmCache[fmCacheCount * 4 + 1] = fm.bottom; - fmCache[fmCacheCount * 4 + 2] = fm.ascent; - fmCache[fmCacheCount * 4 + 3] = fm.descent; - fmCacheCount++; + // measurement has to be done before performing line breaking + // but we don't want to recompute fontmetrics or span ranges the + // second time, so we cache those and then use those stored values + int fmCacheCount = 0; + int spanEndCacheCount = 0; + for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) { + if (fmCacheCount * 4 >= fmCache.length) { + int[] grow = new int[fmCacheCount * 4 * 2]; + System.arraycopy(fmCache, 0, grow, 0, fmCacheCount * 4); + fmCache = grow; + } - spanEndCache[spanEndCacheCount] = spanEnd; - spanEndCacheCount++; - } + if (spanEndCacheCount >= spanEndCache.length) { + int[] grow = new int[spanEndCacheCount * 2]; + System.arraycopy(spanEndCache, 0, grow, 0, spanEndCacheCount); + spanEndCache = grow; + } - int breakCount = nComputeLineBreaks(b.mNativePtr, lineBreaks, lineBreaks.breaks, - lineBreaks.widths, lineBreaks.ascents, lineBreaks.descents, lineBreaks.flags, - lineBreaks.breaks.length, widths); - - final int[] breaks = lineBreaks.breaks; - final float[] lineWidths = lineBreaks.widths; - final float[] ascents = lineBreaks.ascents; - final float[] descents = lineBreaks.descents; - final int[] flags = lineBreaks.flags; - - final int remainingLineCount = mMaximumVisibleLineCount - mLineCount; - final boolean ellipsisMayBeApplied = ellipsize != null - && (ellipsize == TextUtils.TruncateAt.END - || (mMaximumVisibleLineCount == 1 - && ellipsize != TextUtils.TruncateAt.MARQUEE)); - if (0 < remainingLineCount && remainingLineCount < breakCount - && ellipsisMayBeApplied) { - // Calculate width and flag. - float width = 0; - int flag = 0; // XXX May need to also have starting hyphen edit - for (int i = remainingLineCount - 1; i < breakCount; i++) { - if (i == breakCount - 1) { - width += lineWidths[i]; + if (spanned == null) { + spanEnd = paraEnd; + int spanLen = spanEnd - spanStart; + measured.addStyleRun(paint, spanLen, fm, nativePtr); } else { - for (int j = (i == 0 ? 0 : breaks[i - 1]); j < breaks[i]; j++) { - width += widths[j]; - } + spanEnd = spanned.nextSpanTransition(spanStart, paraEnd, + MetricAffectingSpan.class); + int spanLen = spanEnd - spanStart; + MetricAffectingSpan[] spans = + spanned.getSpans(spanStart, spanEnd, MetricAffectingSpan.class); + spans = TextUtils.removeEmptySpans(spans, spanned, + MetricAffectingSpan.class); + measured.addStyleRun(paint, spans, spanLen, fm, nativePtr); } - flag |= flags[i] & TAB_MASK; - } - // Treat the last line and overflowed lines as a single line. - breaks[remainingLineCount - 1] = breaks[breakCount - 1]; - lineWidths[remainingLineCount - 1] = width; - flags[remainingLineCount - 1] = flag; - breakCount = remainingLineCount; - } + // the order of storage here (top, bottom, ascent, descent) has to match the + // code below where these values are retrieved + fmCache[fmCacheCount * 4 + 0] = fm.top; + fmCache[fmCacheCount * 4 + 1] = fm.bottom; + fmCache[fmCacheCount * 4 + 2] = fm.ascent; + fmCache[fmCacheCount * 4 + 3] = fm.descent; + fmCacheCount++; - // here is the offset of the starting character of the line we are currently measuring - int here = paraStart; - - int fmTop = 0, fmBottom = 0, fmAscent = 0, fmDescent = 0; - int fmCacheIndex = 0; - int spanEndCacheIndex = 0; - int breakIndex = 0; - for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) { - // retrieve end of span - spanEnd = spanEndCache[spanEndCacheIndex++]; - - // retrieve cached metrics, order matches above - fm.top = fmCache[fmCacheIndex * 4 + 0]; - fm.bottom = fmCache[fmCacheIndex * 4 + 1]; - fm.ascent = fmCache[fmCacheIndex * 4 + 2]; - fm.descent = fmCache[fmCacheIndex * 4 + 3]; - fmCacheIndex++; - - if (fm.top < fmTop) { - fmTop = fm.top; - } - if (fm.ascent < fmAscent) { - fmAscent = fm.ascent; - } - if (fm.descent > fmDescent) { - fmDescent = fm.descent; - } - if (fm.bottom > fmBottom) { - fmBottom = fm.bottom; + spanEndCache[spanEndCacheCount] = spanEnd; + spanEndCacheCount++; } - // skip breaks ending before current span range - while (breakIndex < breakCount && paraStart + breaks[breakIndex] < spanStart) { - breakIndex++; + int breakCount = nComputeLineBreaks( + nativePtr, + + // Inputs + chs, + paraEnd - paraStart, + firstWidth, + firstWidthLineCount, + restWidth, + variableTabStops, + TAB_INCREMENT, + mLineCount, + + // Outputs + lineBreaks, + lineBreaks.breaks.length, + lineBreaks.breaks, + lineBreaks.widths, + lineBreaks.ascents, + lineBreaks.descents, + lineBreaks.flags, + widths); + + final int[] breaks = lineBreaks.breaks; + final float[] lineWidths = lineBreaks.widths; + final float[] ascents = lineBreaks.ascents; + final float[] descents = lineBreaks.descents; + final int[] flags = lineBreaks.flags; + + final int remainingLineCount = mMaximumVisibleLineCount - mLineCount; + final boolean ellipsisMayBeApplied = ellipsize != null + && (ellipsize == TextUtils.TruncateAt.END + || (mMaximumVisibleLineCount == 1 + && ellipsize != TextUtils.TruncateAt.MARQUEE)); + if (0 < remainingLineCount && remainingLineCount < breakCount + && ellipsisMayBeApplied) { + // Calculate width and flag. + float width = 0; + int flag = 0; // XXX May need to also have starting hyphen edit + for (int i = remainingLineCount - 1; i < breakCount; i++) { + if (i == breakCount - 1) { + width += lineWidths[i]; + } else { + for (int j = (i == 0 ? 0 : breaks[i - 1]); j < breaks[i]; j++) { + width += widths[j]; + } + } + flag |= flags[i] & TAB_MASK; + } + // Treat the last line and overflowed lines as a single line. + breaks[remainingLineCount - 1] = breaks[breakCount - 1]; + lineWidths[remainingLineCount - 1] = width; + flags[remainingLineCount - 1] = flag; + + breakCount = remainingLineCount; } - while (breakIndex < breakCount && paraStart + breaks[breakIndex] <= spanEnd) { - int endPos = paraStart + breaks[breakIndex]; - - boolean moreChars = (endPos < bufEnd); - - final int ascent = fallbackLineSpacing - ? Math.min(fmAscent, Math.round(ascents[breakIndex])) - : fmAscent; - final int descent = fallbackLineSpacing - ? Math.max(fmDescent, Math.round(descents[breakIndex])) - : fmDescent; - v = out(source, here, endPos, - ascent, descent, fmTop, fmBottom, - v, spacingmult, spacingadd, chooseHt, chooseHtv, fm, flags[breakIndex], - needMultiply, chdirs, dir, easy, bufEnd, includepad, trackpad, - addLastLineSpacing, chs, widths, paraStart, ellipsize, - ellipsizedWidth, lineWidths[breakIndex], paint, moreChars); - - if (endPos < spanEnd) { - // preserve metrics for current span + // here is the offset of the starting character of the line we are currently + // measuring + int here = paraStart; + + int fmTop = 0, fmBottom = 0, fmAscent = 0, fmDescent = 0; + int fmCacheIndex = 0; + int spanEndCacheIndex = 0; + int breakIndex = 0; + for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) { + // retrieve end of span + spanEnd = spanEndCache[spanEndCacheIndex++]; + + // retrieve cached metrics, order matches above + fm.top = fmCache[fmCacheIndex * 4 + 0]; + fm.bottom = fmCache[fmCacheIndex * 4 + 1]; + fm.ascent = fmCache[fmCacheIndex * 4 + 2]; + fm.descent = fmCache[fmCacheIndex * 4 + 3]; + fmCacheIndex++; + + if (fm.top < fmTop) { fmTop = fm.top; - fmBottom = fm.bottom; + } + if (fm.ascent < fmAscent) { fmAscent = fm.ascent; + } + if (fm.descent > fmDescent) { fmDescent = fm.descent; - } else { - fmTop = fmBottom = fmAscent = fmDescent = 0; } + if (fm.bottom > fmBottom) { + fmBottom = fm.bottom; + } + + // skip breaks ending before current span range + while (breakIndex < breakCount && paraStart + breaks[breakIndex] < spanStart) { + breakIndex++; + } + + while (breakIndex < breakCount && paraStart + breaks[breakIndex] <= spanEnd) { + int endPos = paraStart + breaks[breakIndex]; + + boolean moreChars = (endPos < bufEnd); + + final int ascent = fallbackLineSpacing + ? Math.min(fmAscent, Math.round(ascents[breakIndex])) + : fmAscent; + final int descent = fallbackLineSpacing + ? Math.max(fmDescent, Math.round(descents[breakIndex])) + : fmDescent; + v = out(source, here, endPos, + ascent, descent, fmTop, fmBottom, + v, spacingmult, spacingadd, chooseHt, chooseHtv, fm, + flags[breakIndex], needMultiply, chdirs, dir, easy, bufEnd, + includepad, trackpad, addLastLineSpacing, chs, widths, paraStart, + ellipsize, ellipsizedWidth, lineWidths[breakIndex], paint, + moreChars); + + if (endPos < spanEnd) { + // preserve metrics for current span + fmTop = fm.top; + fmBottom = fm.bottom; + fmAscent = fm.ascent; + fmDescent = fm.descent; + } else { + fmTop = fmBottom = fmAscent = fmDescent = 0; + } - here = endPos; - breakIndex++; + here = endPos; + breakIndex++; - if (mLineCount >= mMaximumVisibleLineCount && mEllipsized) { - return; + if (mLineCount >= mMaximumVisibleLineCount && mEllipsized) { + return; + } } } - } - if (paraEnd == bufEnd) - break; - } + if (paraEnd == bufEnd) { + break; + } + } - if ((bufEnd == bufStart || source.charAt(bufEnd - 1) == CHAR_NEW_LINE) && - mLineCount < mMaximumVisibleLineCount) { - measured.setPara(source, bufEnd, bufEnd, textDir, b); - - paint.getFontMetricsInt(fm); - - v = out(source, - bufEnd, bufEnd, fm.ascent, fm.descent, - fm.top, fm.bottom, - v, - spacingmult, spacingadd, null, - null, fm, 0, - needMultiply, measured.mLevels, measured.mDir, measured.mEasy, bufEnd, - includepad, trackpad, addLastLineSpacing, null, - null, bufStart, ellipsize, - ellipsizedWidth, 0, paint, false); + if ((bufEnd == bufStart || source.charAt(bufEnd - 1) == CHAR_NEW_LINE) + && mLineCount < mMaximumVisibleLineCount) { + measured.setPara(source, bufEnd, bufEnd, textDir); + + paint.getFontMetricsInt(fm); + + v = out(source, + bufEnd, bufEnd, fm.ascent, fm.descent, + fm.top, fm.bottom, + v, + spacingmult, spacingadd, null, + null, fm, 0, + needMultiply, measured.mLevels, measured.mDir, measured.mEasy, bufEnd, + includepad, trackpad, addLastLineSpacing, null, + null, bufStart, ellipsize, + ellipsizedWidth, 0, paint, false); + } + } finally { + nFinish(nativePtr); } } @@ -1487,26 +1473,51 @@ public class StaticLayout extends Layout { mMaxLineHeight : super.getHeight(); } - private static native long nNewBuilder(); - private static native void nFreeBuilder(long nativePtr); - private static native void nFinishBuilder(long nativePtr); - - // Set up paragraph text and settings; done as one big method to minimize jni crossings - private static native void nSetupParagraph( - /* non zero */ long nativePtr, @NonNull char[] text, @IntRange(from = 0) int length, - @FloatRange(from = 0.0f) float firstWidth, @IntRange(from = 0) int firstWidthLineCount, - @FloatRange(from = 0.0f) float restWidth, @Nullable int[] variableTabStops, - int defaultTabStop, @BreakStrategy int breakStrategy, - @HyphenationFrequency int hyphenationFrequency, boolean isJustified, - @Nullable int[] indents, @Nullable int[] leftPaddings, @Nullable int[] rightPaddings, - @IntRange(from = 0) int indentsOffset); - - // TODO: Make this method CriticalNative once native code defers doing layouts. + /** + * Measurement and break iteration is done in native code. The protocol for using + * the native code is as follows. + * + * First, call nInit to setup native line breaker object. Then, for each paragraph, do the + * following: + * + * - Call one of the following methods for each run within the paragraph depending on the type + * of run: + * + addStyleRun (a text run, to be measured in native code) + * + addReplacementRun (a replacement run, width is given) + * + * - Run nComputeLineBreaks() to obtain line breaks for the paragraph. + * + * After all paragraphs, call finish() to release expensive buffers. + */ + + /* package */ static void addStyleRun(long nativePtr, TextPaint paint, int start, int end, + boolean isRtl) { + nAddStyleRun(nativePtr, paint.getNativeInstance(), start, end, isRtl); + } + + /* package */ static void addReplacementRun(long nativePtr, TextPaint paint, int start, int end, + float width) { + nAddReplacementRun(nativePtr, paint.getNativeInstance(), start, end, width); + } + + @FastNative + private static native long nInit( + @BreakStrategy int breakStrategy, + @HyphenationFrequency int hyphenationFrequency, + boolean isJustified, + @Nullable int[] indents, + @Nullable int[] leftPaddings, + @Nullable int[] rightPaddings); + + @CriticalNative + private static native void nFinish(long nativePtr); + + @CriticalNative private static native void nAddStyleRun( /* non-zero */ long nativePtr, /* non-zero */ long nativePaint, @IntRange(from = 0) int start, @IntRange(from = 0) int end, boolean isRtl); - // TODO: Make this method CriticalNative once native code defers doing layouts. + @CriticalNative private static native void nAddReplacementRun( /* non-zero */ long nativePtr, /* non-zero */ long nativePaint, @IntRange(from = 0) int start, @IntRange(from = 0) int end, @@ -1519,10 +1530,28 @@ public class StaticLayout extends Layout { // arrays do not have to be resized // The individual character widths will be returned in charWidths. The length of charWidths must // be at least the length of the text. - private static native int nComputeLineBreaks(long nativePtr, LineBreaks recycle, - int[] recycleBreaks, float[] recycleWidths, float[] recycleAscents, - float[] recycleDescents, int[] recycleFlags, int recycleLength, - float[] charWidths); + private static native int nComputeLineBreaks( + /* non zero */ long nativePtr, + + // Inputs + @NonNull char[] text, + @IntRange(from = 0) int length, + @FloatRange(from = 0.0f) float firstWidth, + @IntRange(from = 0) int firstWidthLineCount, + @FloatRange(from = 0.0f) float restWidth, + @Nullable int[] variableTabStops, + int defaultTabStop, + @IntRange(from = 0) int indentsOffset, + + // Outputs + @NonNull LineBreaks recycle, + @IntRange(from = 0) int recycleLength, + @NonNull int[] recycleBreaks, + @NonNull float[] recycleWidths, + @NonNull float[] recycleAscents, + @NonNull float[] recycleDescents, + @NonNull int[] recycleFlags, + @NonNull float[] charWidths); private int mLineCount; private int mTopPadding, mBottomPadding; diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java index 20c0ed87285a..86cc0141b0a4 100644 --- a/core/java/android/text/TextLine.java +++ b/core/java/android/text/TextLine.java @@ -28,6 +28,7 @@ import android.text.style.MetricAffectingSpan; import android.text.style.ReplacementSpan; import android.util.Log; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import java.util.ArrayList; @@ -44,7 +45,8 @@ import java.util.ArrayList; * * @hide */ -class TextLine { +@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) +public class TextLine { private static final boolean DEBUG = false; private TextPaint mPaint; @@ -82,7 +84,8 @@ class TextLine { * * @return an uninitialized TextLine */ - static TextLine obtain() { + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + public static TextLine obtain() { TextLine tl; synchronized (sCached) { for (int i = sCached.length; --i >= 0;) { @@ -107,7 +110,8 @@ class TextLine { * @return null, as a convenience from clearing references to the provided * TextLine */ - static TextLine recycle(TextLine tl) { + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + public static TextLine recycle(TextLine tl) { tl.mText = null; tl.mPaint = null; tl.mDirections = null; @@ -142,7 +146,8 @@ class TextLine { * @param hasTabs true if the line might contain tabs * @param tabStops the tabStops. Can be null. */ - void set(TextPaint paint, CharSequence text, int start, int limit, int dir, + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + public void set(TextPaint paint, CharSequence text, int start, int limit, int dir, Directions directions, boolean hasTabs, TabStops tabStops) { mPaint = paint; mText = text; @@ -196,7 +201,8 @@ class TextLine { /** * Justify the line to the given width. */ - void justify(float justifyWidth) { + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + public void justify(float justifyWidth) { int end = mLen; while (end > 0 && isLineEndSpace(mText.charAt(mStart + end - 1))) { end--; @@ -277,7 +283,8 @@ class TextLine { * @param fmi receives font metrics information, can be null * @return the signed width of the line */ - float metrics(FontMetricsInt fmi) { + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + public float metrics(FontMetricsInt fmi) { return measure(mLen, false, fmi); } @@ -1165,23 +1172,18 @@ class TextLine { } private boolean isStretchableWhitespace(int ch) { - // TODO: Support other stretchable whitespace. (Bug: 34013491) - return ch == 0x0020 || ch == 0x00A0; - } - - private int nextStretchableSpace(int start, int end) { - for (int i = start; i < end; i++) { - final char c = mCharsValid ? mChars[i] : mText.charAt(i + mStart); - if (isStretchableWhitespace(c)) return i; - } - return end; + // TODO: Support NBSP and other stretchable whitespace (b/34013491 and b/68204709). + return ch == 0x0020; } /* Return the number of spaces in the text line, for the purpose of justification */ private int countStretchableSpaces(int start, int end) { int count = 0; - for (int i = start; i < end; i = nextStretchableSpace(i + 1, end)) { - count++; + for (int i = start; i < end; i++) { + final char c = mCharsValid ? mChars[i] : mText.charAt(i + mStart); + if (isStretchableWhitespace(c)) { + count++; + } } return count; } diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java index 68afeecfc996..cbdaa69b5aa0 100644 --- a/core/java/android/text/TextUtils.java +++ b/core/java/android/text/TextUtils.java @@ -1519,7 +1519,7 @@ public class TextUtils { } // XXX this is probably ok, but need to look at it more - tempMt.setPara(format, 0, format.length(), textDir, null); + tempMt.setPara(format, 0, format.length(), textDir); float moreWid = tempMt.addStyleRun(p, tempMt.mLen, null); if (w + moreWid <= avail) { @@ -1541,7 +1541,7 @@ public class TextUtils { private static float setPara(MeasuredText mt, TextPaint paint, CharSequence text, int start, int end, TextDirectionHeuristic textDir) { - mt.setPara(text, start, end, textDir, null); + mt.setPara(text, start, end, textDir); float width; Spanned sp = text instanceof Spanned ? (Spanned) text : null; diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java index 8c3b8a2e6b20..26d2141e3486 100644 --- a/core/java/android/view/textclassifier/TextClassification.java +++ b/core/java/android/view/textclassifier/TextClassification.java @@ -33,6 +33,52 @@ import java.util.List; /** * Information for generating a widget to handle classified text. + * + * <p>A TextClassification object contains icons, labels, onClickListeners and intents that may + * be used to build a widget that can be used to act on classified text. + * + * <p>e.g. building a view that, when clicked, shares the classified text with the preferred app: + * + * <pre>{@code + * // Called preferably outside the UiThread. + * TextClassification classification = textClassifier.classifyText(allText, 10, 25, null); + * + * // Called on the UiThread. + * Button button = new Button(context); + * button.setCompoundDrawablesWithIntrinsicBounds(classification.getIcon(), null, null, null); + * button.setText(classification.getLabel()); + * button.setOnClickListener(classification.getOnClickListener()); + * }</pre> + * + * <p>e.g. starting an action mode with menu items that can handle the classified text: + * + * <pre>{@code + * // Called preferably outside the UiThread. + * final TextClassification classification = textClassifier.classifyText(allText, 10, 25, null); + * + * // Called on the UiThread. + * view.startActionMode(new ActionMode.Callback() { + * + * public boolean onCreateActionMode(ActionMode mode, Menu menu) { + * for (int i = 0; i < classification.getActionCount(); i++) { + * if (thisAppHasPermissionToInvokeIntent(classification.getIntent(i))) { + * menu.add(Menu.NONE, i, 20, classification.getLabel(i)) + * .setIcon(classification.getIcon(i)) + * .setIntent(classification.getIntent(i)); + * } + * } + * return true; + * } + * + * public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + * context.startActivity(item.getIntent()); + * return true; + * } + * + * ... + * }); + * }</pre> + * */ public final class TextClassification { diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java index c3601d9d32be..46dbd0e34a1c 100644 --- a/core/java/android/view/textclassifier/TextClassifier.java +++ b/core/java/android/view/textclassifier/TextClassifier.java @@ -29,8 +29,8 @@ import java.lang.annotation.RetentionPolicy; /** * Interface for providing text classification related features. * - * <p>Unless otherwise stated, methods of this interface are blocking operations and you should - * avoid calling them on the UI thread. + * <p>Unless otherwise stated, methods of this interface are blocking operations. + * Avoid calling them on the UI thread. */ public interface TextClassifier { @@ -75,8 +75,8 @@ public interface TextClassifier { }; /** - * Returns suggested text selection indices, recognized types and their associated confidence - * scores. The selections are ordered from highest to lowest scoring. + * 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. * * @param text text providing context for the selected text (which is specified * by the sub sequence starting at selectionStartIndex and ending at selectionEndIndex) diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java index 95cb4549cec1..48e427fd53b5 100644 --- a/core/java/android/webkit/WebViewFactory.java +++ b/core/java/android/webkit/WebViewFactory.java @@ -25,7 +25,6 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.Signature; -import android.os.Build; import android.os.RemoteException; import android.os.ServiceManager; import android.os.StrictMode; @@ -445,38 +444,17 @@ public final class WebViewFactory { } } - private static int prepareWebViewInSystemServer(String[] nativeLibraryPaths) { - if (DEBUG) Log.v(LOGTAG, "creating relro files"); - int numRelros = 0; - - // We must always trigger createRelRo regardless of the value of nativeLibraryPaths. Any - // unexpected values will be handled there to ensure that we trigger notifying any process - // waiting on relro creation. - if (Build.SUPPORTED_32_BIT_ABIS.length > 0) { - if (DEBUG) Log.v(LOGTAG, "Create 32 bit relro"); - WebViewLibraryLoader.createRelroFile(false /* is64Bit */, nativeLibraryPaths[0]); - numRelros++; - } - - if (Build.SUPPORTED_64_BIT_ABIS.length > 0) { - if (DEBUG) Log.v(LOGTAG, "Create 64 bit relro"); - WebViewLibraryLoader.createRelroFile(true /* is64Bit */, nativeLibraryPaths[1]); - numRelros++; - } - return numRelros; - } - /** * @hide */ public static int onWebViewProviderChanged(PackageInfo packageInfo) { - String[] nativeLibs = null; + int startedRelroProcesses = 0; ApplicationInfo originalAppInfo = new ApplicationInfo(packageInfo.applicationInfo); try { fixupStubApplicationInfo(packageInfo.applicationInfo, AppGlobals.getInitialApplication().getPackageManager()); - nativeLibs = WebViewLibraryLoader.updateWebViewZygoteVmSize(packageInfo); + startedRelroProcesses = WebViewLibraryLoader.prepareNativeLibraries(packageInfo); } catch (Throwable t) { // Log and discard errors at this stage as we must not crash the system server. Log.e(LOGTAG, "error preparing webview native library", t); @@ -484,7 +462,7 @@ public final class WebViewFactory { WebViewZygote.onWebViewProviderChanged(packageInfo, originalAppInfo); - return prepareWebViewInSystemServer(nativeLibs); + return startedRelroProcesses; } private static String WEBVIEW_UPDATE_SERVICE_NAME = "webviewupdate"; diff --git a/core/java/android/webkit/WebViewLibraryLoader.java b/core/java/android/webkit/WebViewLibraryLoader.java index fa1a3907b1f9..175f35f41ebf 100644 --- a/core/java/android/webkit/WebViewLibraryLoader.java +++ b/core/java/android/webkit/WebViewLibraryLoader.java @@ -127,11 +127,34 @@ class WebViewLibraryLoader { } } + static int prepareNativeLibraries(PackageInfo webviewPackageInfo) + throws WebViewFactory.MissingWebViewPackageException { + String[] nativeLibs = updateWebViewZygoteVmSize(webviewPackageInfo); + if (DEBUG) Log.v(LOGTAG, "creating relro files"); + int numRelros = 0; + + // We must always trigger createRelRo regardless of the value of nativeLibraryPaths. Any + // unexpected values will be handled there to ensure that we trigger notifying any process + // waiting on relro creation. + if (Build.SUPPORTED_32_BIT_ABIS.length > 0) { + if (DEBUG) Log.v(LOGTAG, "Create 32 bit relro"); + createRelroFile(false /* is64Bit */, nativeLibs[0]); + numRelros++; + } + + if (Build.SUPPORTED_64_BIT_ABIS.length > 0) { + if (DEBUG) Log.v(LOGTAG, "Create 64 bit relro"); + createRelroFile(true /* is64Bit */, nativeLibs[1]); + numRelros++; + } + return numRelros; + } + /** * * @return the native WebView libraries in the new WebView APK. */ - static String[] updateWebViewZygoteVmSize(PackageInfo packageInfo) + private static String[] updateWebViewZygoteVmSize(PackageInfo packageInfo) throws WebViewFactory.MissingWebViewPackageException { // Find the native libraries of the new WebView package, to change the size of the // memory region in the Zygote reserved for the library. diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 384f4f8393cf..e6da69dc95c7 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -129,7 +129,7 @@ import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.List; - +import java.util.Map; /** * Helper class used by TextView to handle editable text views. @@ -163,9 +163,9 @@ public class Editor { private static final int MENU_ITEM_ORDER_REPLACE = 9; private static final int MENU_ITEM_ORDER_AUTOFILL = 10; private static final int MENU_ITEM_ORDER_PASTE_AS_PLAIN_TEXT = 11; + private static final int MENU_ITEM_ORDER_SECONDARY_ASSIST_ACTIONS_START = 50; private static final int MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START = 100; - private static final float MAGNIFIER_ZOOM = 1.25f; @IntDef({MagnifierHandleTrigger.SELECTION_START, MagnifierHandleTrigger.SELECTION_END, MagnifierHandleTrigger.INSERTION}) @@ -3793,6 +3793,7 @@ public class Editor { private final RectF mSelectionBounds = new RectF(); private final boolean mHasSelection; private final int mHandleHeight; + private final Map<MenuItem, OnClickListener> mAssistClickHandlers = new HashMap<>(); public TextActionModeCallback(boolean hasSelection) { mHasSelection = hasSelection; @@ -3820,6 +3821,8 @@ public class Editor { @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { + mAssistClickHandlers.clear(); + mode.setTitle(null); mode.setSubtitle(null); mode.setTitleOptionalHint(true); @@ -3903,14 +3906,14 @@ public class Editor { updateSelectAllItem(menu); updateReplaceItem(menu); - updateAssistMenuItem(menu); + updateAssistMenuItems(menu); } @Override public boolean onPrepareActionMode(ActionMode mode, Menu menu) { updateSelectAllItem(menu); updateReplaceItem(menu); - updateAssistMenuItem(menu); + updateAssistMenuItems(menu); Callback customCallback = getCustomCallback(); if (customCallback != null) { @@ -3943,32 +3946,118 @@ public class Editor { } } - private void updateAssistMenuItem(Menu menu) { - menu.removeItem(TextView.ID_ASSIST); + private void updateAssistMenuItems(Menu menu) { + clearAssistMenuItems(menu); + if (!mTextView.isDeviceProvisioned()) { + return; + } final TextClassification textClassification = getSelectionActionModeHelper().getTextClassification(); - if (canAssist()) { - menu.add(TextView.ID_ASSIST, TextView.ID_ASSIST, MENU_ITEM_ORDER_ASSIST, - textClassification.getLabel()) - .setIcon(textClassification.getIcon()) - .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); - mMetricsLogger.write( - new LogMaker(MetricsEvent.TEXT_SELECTION_MENU_ITEM_ASSIST) - .setType(MetricsEvent.TYPE_OPEN) - .setSubtype(textClassification.getLogType())); + final int count = textClassification != null ? textClassification.getActionCount() : 0; + for (int i = 0; i < count; i++) { + if (!isValidAssistMenuItem(i)) { + continue; + } + final int groupId = TextView.ID_ASSIST; + final int order = (i == 0) + ? MENU_ITEM_ORDER_ASSIST + : MENU_ITEM_ORDER_SECONDARY_ASSIST_ACTIONS_START + i; + final int id = (i == 0) ? TextView.ID_ASSIST : Menu.NONE; + final int showAsFlag = (i == 0) + ? MenuItem.SHOW_AS_ACTION_ALWAYS + : MenuItem.SHOW_AS_ACTION_NEVER; + final MenuItem item = menu.add( + groupId, id, order, textClassification.getLabel(i)) + .setIcon(textClassification.getIcon(i)) + .setIntent(textClassification.getIntent(i)); + item.setShowAsAction(showAsFlag); + mAssistClickHandlers.put(item, textClassification.getOnClickListener(i)); + if (id == TextView.ID_ASSIST) { + mMetricsLogger.write( + new LogMaker(MetricsEvent.TEXT_SELECTION_MENU_ITEM_ASSIST) + .setType(MetricsEvent.TYPE_OPEN) + .setSubtype(textClassification.getLogType())); + } + } + } + + private void clearAssistMenuItems(Menu menu) { + int i = 0; + while (i < menu.size()) { + final MenuItem menuItem = menu.getItem(i); + if (menuItem.getGroupId() == TextView.ID_ASSIST) { + menu.removeItem(menuItem.getItemId()); + continue; + } + i++; + } + } + + private boolean isValidAssistMenuItem(int index) { + final TextClassification textClassification = + getSelectionActionModeHelper().getTextClassification(); + if (!mTextView.isDeviceProvisioned() || textClassification == null + || index < 0 || index >= textClassification.getActionCount()) { + return false; + } + final Drawable icon = textClassification.getIcon(index); + final CharSequence label = textClassification.getLabel(index); + final boolean hasUi = icon != null || !TextUtils.isEmpty(label); + final OnClickListener onClick = textClassification.getOnClickListener(index); + final Intent intent = textClassification.getIntent(index); + final boolean hasAction = onClick != null || isSupportedIntent(intent); + return hasUi && hasAction; + } + + private boolean isSupportedIntent(Intent intent) { + if (intent == null) { + return false; + } + final Context context = mTextView.getContext(); + final ResolveInfo info = context.getPackageManager().resolveActivity(intent, 0); + final boolean samePackage = context.getPackageName().equals( + info.activityInfo.packageName); + if (samePackage) { + return true; } + + final boolean exported = info.activityInfo.exported; + final boolean requiresPermission = info.activityInfo.permission != null; + final boolean hasPermission = !requiresPermission + || context.checkSelfPermission(info.activityInfo.permission) + == PackageManager.PERMISSION_GRANTED; + return exported && hasPermission; } - private boolean canAssist() { + private boolean onAssistMenuItemClicked(MenuItem assistMenuItem) { + Preconditions.checkArgument(assistMenuItem.getGroupId() == TextView.ID_ASSIST); + final TextClassification textClassification = getSelectionActionModeHelper().getTextClassification(); - return mTextView.isDeviceProvisioned() - && textClassification != null - && (textClassification.getIcon() != null - || !TextUtils.isEmpty(textClassification.getLabel())) - && (textClassification.getOnClickListener() != null - || (textClassification.getIntent() != null - && mTextView.getContext().canStartActivityForResult())); + if (!mTextView.isDeviceProvisioned() || textClassification == null) { + // No textClassification result to handle the click. Eat the click. + return true; + } + + OnClickListener onClickListener = mAssistClickHandlers.get(assistMenuItem); + if (onClickListener == null) { + final Intent intent = assistMenuItem.getIntent(); + if (intent != null) { + onClickListener = TextClassification.createStartActivityOnClickListener( + mTextView.getContext(), intent); + } + } + if (onClickListener != null) { + onClickListener.onClick(mTextView); + stopTextActionMode(); + if (assistMenuItem.getItemId() == TextView.ID_ASSIST) { + mMetricsLogger.action( + MetricsEvent.ACTION_TEXT_SELECTION_MENU_ITEM_ASSIST, + textClassification.getLogType()); + } + } + // We tried our best. + return true; } @Override @@ -3982,25 +4071,7 @@ public class Editor { if (customCallback != null && customCallback.onActionItemClicked(mode, item)) { return true; } - final TextClassification textClassification = - getSelectionActionModeHelper().getTextClassification(); - if (TextView.ID_ASSIST == item.getItemId() && textClassification != null) { - final OnClickListener onClickListener = - textClassification.getOnClickListener(); - if (onClickListener != null) { - onClickListener.onClick(mTextView); - } else { - final Intent intent = textClassification.getIntent(); - if (intent != null) { - TextClassification.createStartActivityOnClickListener( - mTextView.getContext(), intent) - .onClick(mTextView); - } - } - mMetricsLogger.action( - MetricsEvent.ACTION_TEXT_SELECTION_MENU_ITEM_ASSIST, - textClassification.getLogType()); - stopTextActionMode(); + if (item.getGroupId() == TextView.ID_ASSIST && onAssistMenuItemClicked(item)) { return true; } return mTextView.onTextContextMenuItem(item.getItemId()); @@ -4029,6 +4100,8 @@ public class Editor { if (mSelectionModifierCursorController != null) { mSelectionModifierCursorController.hide(); } + + mAssistClickHandlers.clear(); } @Override @@ -4547,7 +4620,7 @@ public class Editor { + mTextView.getTotalPaddingTop() - mTextView.getScrollY(); suspendBlink(); - mMagnifier.show(xPosInView, yPosInView, MAGNIFIER_ZOOM); + mMagnifier.show(xPosInView, yPosInView); } protected final void dismissMagnifier() { @@ -6560,6 +6633,9 @@ public class Editor { private void loadSupportedActivities() { mSupportedActivities.clear(); + if (!mContext.canStartActivityForResult()) { + return; + } PackageManager packageManager = mTextView.getContext().getPackageManager(); List<ResolveInfo> unfiltered = packageManager.queryIntentActivities(createProcessTextIntent(), 0); diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 239d7091ed3d..9cc1959f296c 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -63,6 +63,7 @@ import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; import android.util.SparseLongArray; +import android.util.StatsLog; import android.util.TimeUtils; import android.util.Xml; import android.view.Display; @@ -4030,6 +4031,7 @@ public class BatteryStatsImpl extends BatteryStats { } addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_LONG_WAKE_LOCK_START, historyName, uid); + StatsLog.write(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED, uid, name, historyName, 1); } public void noteLongPartialWakelockFinish(String name, String historyName, int uid) { @@ -4045,6 +4047,7 @@ public class BatteryStatsImpl extends BatteryStats { } addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_LONG_WAKE_LOCK_FINISH, historyName, uid); + StatsLog.write(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED, uid, name, historyName, 0); } void aggregateLastWakeupUptimeLocked(long uptimeMs) { @@ -4403,6 +4406,7 @@ public class BatteryStatsImpl extends BatteryStats { mPowerSaveModeEnabledTimer.stopRunningLocked(elapsedRealtime); } addHistoryRecordLocked(elapsedRealtime, uptime); + StatsLog.write(StatsLog.BATTERY_SAVER_MODE_STATE_CHANGED, enabled ? 1 : 0); } } @@ -6180,17 +6184,25 @@ public class BatteryStatsImpl extends BatteryStats { public void noteAudioTurnedOnLocked(long elapsedRealtimeMs) { createAudioTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs); + // TODO(statsd): Possibly use a worksource instead of a uid. + StatsLog.write(StatsLog.AUDIO_STATE_CHANGED, getUid(), 1); } public void noteAudioTurnedOffLocked(long elapsedRealtimeMs) { if (mAudioTurnedOnTimer != null) { mAudioTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs); + if (!mAudioTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped + // TODO(statsd): Possibly use a worksource instead of a uid. + StatsLog.write(StatsLog.AUDIO_STATE_CHANGED, getUid(), 0); + } } } public void noteResetAudioLocked(long elapsedRealtimeMs) { if (mAudioTurnedOnTimer != null) { mAudioTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs); + // TODO(statsd): Possibly use a worksource instead of a uid. + StatsLog.write(StatsLog.AUDIO_STATE_CHANGED, getUid(), 0); } } @@ -6204,17 +6216,25 @@ public class BatteryStatsImpl extends BatteryStats { public void noteVideoTurnedOnLocked(long elapsedRealtimeMs) { createVideoTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs); + // TODO(statsd): Possibly use a worksource instead of a uid. + StatsLog.write(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(), 1); } public void noteVideoTurnedOffLocked(long elapsedRealtimeMs) { if (mVideoTurnedOnTimer != null) { mVideoTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs); + if (!mVideoTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped + // TODO(statsd): Possibly use a worksource instead of a uid. + StatsLog.write(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(), 0); + } } } public void noteResetVideoLocked(long elapsedRealtimeMs) { if (mVideoTurnedOnTimer != null) { mVideoTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs); + // TODO(statsd): Possibly use a worksource instead of a uid. + StatsLog.write(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(), 0); } } @@ -6228,17 +6248,25 @@ public class BatteryStatsImpl extends BatteryStats { public void noteFlashlightTurnedOnLocked(long elapsedRealtimeMs) { createFlashlightTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs); + // TODO(statsd): Possibly use a worksource instead of a uid. + StatsLog.write(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), 1); } public void noteFlashlightTurnedOffLocked(long elapsedRealtimeMs) { if (mFlashlightTurnedOnTimer != null) { mFlashlightTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs); + if (!mFlashlightTurnedOnTimer.isRunningLocked()) { + // TODO(statsd): Possibly use a worksource instead of a uid. + StatsLog.write(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), 0); + } } } public void noteResetFlashlightLocked(long elapsedRealtimeMs) { if (mFlashlightTurnedOnTimer != null) { mFlashlightTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs); + // TODO(statsd): Possibly use a worksource instead of a uid. + StatsLog.write(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), 0); } } @@ -6252,17 +6280,25 @@ public class BatteryStatsImpl extends BatteryStats { public void noteCameraTurnedOnLocked(long elapsedRealtimeMs) { createCameraTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs); + // TODO(statsd): Possibly use a worksource instead of a uid. + StatsLog.write(StatsLog.CAMERA_STATE_CHANGED, getUid(), 1); } public void noteCameraTurnedOffLocked(long elapsedRealtimeMs) { if (mCameraTurnedOnTimer != null) { mCameraTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs); + if (!mCameraTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped + // TODO(statsd): Possibly use a worksource instead of a uid. + StatsLog.write(StatsLog.CAMERA_STATE_CHANGED, getUid(), 0); + } } } public void noteResetCameraLocked(long elapsedRealtimeMs) { if (mCameraTurnedOnTimer != null) { mCameraTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs); + // TODO(statsd): Possibly use a worksource instead of a uid. + StatsLog.write(StatsLog.CAMERA_STATE_CHANGED, getUid(), 0); } } @@ -6311,26 +6347,42 @@ public class BatteryStatsImpl extends BatteryStats { public void noteBluetoothScanStartedLocked(long elapsedRealtimeMs, boolean isUnoptimized) { createBluetoothScanTimerLocked().startRunningLocked(elapsedRealtimeMs); + // TODO(statsd): Possibly use a worksource instead of a uid. + StatsLog.write(StatsLog.BLE_SCAN_STATE_CHANGED, getUid(), 1); if (isUnoptimized) { createBluetoothUnoptimizedScanTimerLocked().startRunningLocked(elapsedRealtimeMs); + // TODO(statsd): Possibly use a worksource instead of a uid. + StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, getUid(), 1); } } public void noteBluetoothScanStoppedLocked(long elapsedRealtimeMs, boolean isUnoptimized) { if (mBluetoothScanTimer != null) { mBluetoothScanTimer.stopRunningLocked(elapsedRealtimeMs); + if (!mBluetoothScanTimer.isRunningLocked()) { // only tell statsd if truly stopped + // TODO(statsd): Possibly use a worksource instead of a uid. + StatsLog.write(StatsLog.BLE_SCAN_STATE_CHANGED, getUid(), 0); + } } if (isUnoptimized && mBluetoothUnoptimizedScanTimer != null) { mBluetoothUnoptimizedScanTimer.stopRunningLocked(elapsedRealtimeMs); + if (!mBluetoothUnoptimizedScanTimer.isRunningLocked()) { + // TODO(statsd): Possibly use a worksource instead of a uid. + StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, getUid(), 0); + } } } public void noteResetBluetoothScanLocked(long elapsedRealtimeMs) { if (mBluetoothScanTimer != null) { mBluetoothScanTimer.stopAllRunningLocked(elapsedRealtimeMs); + // TODO(statsd): Possibly use a worksource instead of a uid. + StatsLog.write(StatsLog.BLE_SCAN_STATE_CHANGED, getUid(), 0); } if (mBluetoothUnoptimizedScanTimer != null) { mBluetoothUnoptimizedScanTimer.stopAllRunningLocked(elapsedRealtimeMs); + // TODO(statsd): Possibly use a worksource instead of a uid. + StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, getUid(), 0); } } @@ -6352,6 +6404,9 @@ public class BatteryStatsImpl extends BatteryStats { createBluetoothScanResultCounterLocked().addAtomic(numNewResults); // Uses background timebase, so the count will only be incremented if uid in background. createBluetoothScanResultBgCounterLocked().addAtomic(numNewResults); + // TODO(statsd): Possibly use a worksource instead of a uid. + // TODO(statsd): This could be in AppScanStats instead, if desired. + StatsLog.write(StatsLog.BLE_SCAN_RESULT_RECEIVED, getUid(), numNewResults); } @Override @@ -8725,6 +8780,8 @@ public class BatteryStatsImpl extends BatteryStats { DualTimer t = mSyncStats.startObject(name); if (t != null) { t.startRunningLocked(elapsedRealtimeMs); + // TODO(statsd): Possibly use a worksource instead of a uid. + StatsLog.write(StatsLog.SYNC_STATE_CHANGED, getUid(), name, 1); } } @@ -8732,6 +8789,10 @@ public class BatteryStatsImpl extends BatteryStats { DualTimer t = mSyncStats.stopObject(name); if (t != null) { t.stopRunningLocked(elapsedRealtimeMs); + if (!t.isRunningLocked()) { // only tell statsd if truly stopped + // TODO(statsd): Possibly use a worksource instead of a uid. + StatsLog.write(StatsLog.SYNC_STATE_CHANGED, getUid(), name, 0); + } } } @@ -8739,6 +8800,8 @@ public class BatteryStatsImpl extends BatteryStats { DualTimer t = mJobStats.startObject(name); if (t != null) { t.startRunningLocked(elapsedRealtimeMs); + // TODO(statsd): Possibly use a worksource instead of a uid. + StatsLog.write(StatsLog.SCHEDULED_JOB_STATE_CHANGED, getUid(), name, 1); } } @@ -8746,6 +8809,10 @@ public class BatteryStatsImpl extends BatteryStats { DualTimer t = mJobStats.stopObject(name); if (t != null) { t.stopRunningLocked(elapsedRealtimeMs); + if (!t.isRunningLocked()) { // only tell statsd if truly stopped + // TODO(statsd): Possibly use a worksource instead of a uid. + StatsLog.write(StatsLog.SCHEDULED_JOB_STATE_CHANGED, getUid(), name, 0); + } } if (mBsi.mOnBatteryTimeBase.isRunning()) { SparseIntArray types = mJobCompletions.get(name); @@ -8812,6 +8879,8 @@ public class BatteryStatsImpl extends BatteryStats { } if (type == WAKE_TYPE_PARTIAL) { createAggregatedPartialWakelockTimerLocked().startRunningLocked(elapsedRealtimeMs); + // TODO(statsd): Possibly use a worksource instead of a uid. + StatsLog.write(StatsLog.UID_WAKELOCK_STATE_CHANGED, getUid(), type, 1); if (pid >= 0) { Pid p = getPidStatsLocked(pid); if (p.mWakeNesting++ == 0) { @@ -8829,6 +8898,11 @@ public class BatteryStatsImpl extends BatteryStats { if (type == WAKE_TYPE_PARTIAL) { if (mAggregatedPartialWakelockTimer != null) { mAggregatedPartialWakelockTimer.stopRunningLocked(elapsedRealtimeMs); + if (!mAggregatedPartialWakelockTimer.isRunningLocked()) { + // TODO(statsd): Possibly use a worksource instead of a uid. + StatsLog.write(StatsLog.UID_WAKELOCK_STATE_CHANGED, getUid(), type, + 0); + } } if (pid >= 0) { Pid p = mPids.get(pid); @@ -8852,6 +8926,12 @@ public class BatteryStatsImpl extends BatteryStats { public void noteStartSensor(int sensor, long elapsedRealtimeMs) { DualTimer t = getSensorTimerLocked(sensor, /* create= */ true); t.startRunningLocked(elapsedRealtimeMs); + // TODO(statsd): Possibly use a worksource instead of a uid. + if (sensor == Sensor.GPS) { + StatsLog.write(StatsLog.GPS_SCAN_STATE_CHANGED, getUid(), 1); + } else { + StatsLog.write(StatsLog.SENSOR_STATE_CHANGED, getUid(), sensor, 1); + } } public void noteStopSensor(int sensor, long elapsedRealtimeMs) { @@ -8859,6 +8939,14 @@ public class BatteryStatsImpl extends BatteryStats { DualTimer t = getSensorTimerLocked(sensor, false); if (t != null) { t.stopRunningLocked(elapsedRealtimeMs); + if (!t.isRunningLocked()) { // only tell statsd if truly stopped + // TODO(statsd): Possibly use a worksource instead of a uid. + if (sensor == Sensor.GPS) { + StatsLog.write(StatsLog.GPS_SCAN_STATE_CHANGED, getUid(), 0); + } else { + StatsLog.write(StatsLog.SENSOR_STATE_CHANGED, getUid(), sensor, 0); + } + } } } diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java index 2098ebd8814a..5b65bbe1e90e 100644 --- a/core/java/com/android/internal/view/InputConnectionWrapper.java +++ b/core/java/com/android/internal/view/InputConnectionWrapper.java @@ -36,6 +36,7 @@ import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags; import android.view.inputmethod.InputContentInfo; import java.lang.ref.WeakReference; +import java.util.concurrent.atomic.AtomicBoolean; public class InputConnectionWrapper implements InputConnection { private static final int MAX_WAIT_TIME_MILLIS = 2000; @@ -46,6 +47,14 @@ public class InputConnectionWrapper implements InputConnection { @MissingMethodFlags private final int mMissingMethods; + /** + * {@code true} if the system already decided to take away IME focus from the target app. This + * can be signaled even when the corresponding signal is in the task queue and + * {@link InputMethodService#onUnbindInput()} is not yet called back on the UI thread. + */ + @NonNull + private final AtomicBoolean mIsUnbindIssued; + static class InputContextCallback extends IInputContextCallback.Stub { private static final String TAG = "InputConnectionWrapper.ICC"; public int mSeq; @@ -231,14 +240,20 @@ public class InputConnectionWrapper implements InputConnection { public InputConnectionWrapper( @NonNull WeakReference<AbstractInputMethodService> inputMethodService, - IInputContext inputContext, @MissingMethodFlags final int missingMethods) { + IInputContext inputContext, @MissingMethodFlags final int missingMethods, + @NonNull AtomicBoolean isUnbindIssued) { mInputMethodService = inputMethodService; mIInputContext = inputContext; mMissingMethods = missingMethods; + mIsUnbindIssued = isUnbindIssued; } @AnyThread public CharSequence getTextAfterCursor(int length, int flags) { + if (mIsUnbindIssued.get()) { + return null; + } + CharSequence value = null; try { InputContextCallback callback = InputContextCallback.getInstance(); @@ -258,6 +273,10 @@ public class InputConnectionWrapper implements InputConnection { @AnyThread public CharSequence getTextBeforeCursor(int length, int flags) { + if (mIsUnbindIssued.get()) { + return null; + } + CharSequence value = null; try { InputContextCallback callback = InputContextCallback.getInstance(); @@ -277,6 +296,10 @@ public class InputConnectionWrapper implements InputConnection { @AnyThread public CharSequence getSelectedText(int flags) { + if (mIsUnbindIssued.get()) { + return null; + } + if (isMethodMissing(MissingMethodFlags.GET_SELECTED_TEXT)) { // This method is not implemented. return null; @@ -300,6 +323,10 @@ public class InputConnectionWrapper implements InputConnection { @AnyThread public int getCursorCapsMode(int reqModes) { + if (mIsUnbindIssued.get()) { + return 0; + } + int value = 0; try { InputContextCallback callback = InputContextCallback.getInstance(); @@ -319,6 +346,10 @@ public class InputConnectionWrapper implements InputConnection { @AnyThread public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) { + if (mIsUnbindIssued.get()) { + return null; + } + ExtractedText value = null; try { InputContextCallback callback = InputContextCallback.getInstance(); @@ -516,6 +547,10 @@ public class InputConnectionWrapper implements InputConnection { @AnyThread public boolean requestCursorUpdates(int cursorUpdateMode) { + if (mIsUnbindIssued.get()) { + return false; + } + boolean result = false; if (isMethodMissing(MissingMethodFlags.REQUEST_CURSOR_UPDATES)) { // This method is not implemented. @@ -550,6 +585,10 @@ public class InputConnectionWrapper implements InputConnection { @AnyThread public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) { + if (mIsUnbindIssued.get()) { + return false; + } + boolean result = false; if (isMethodMissing(MissingMethodFlags.COMMIT_CONTENT)) { // This method is not implemented. diff --git a/core/java/com/android/internal/widget/Magnifier.java b/core/java/com/android/internal/widget/Magnifier.java index 6d54d7b8f1e2..f9e98eac9cfe 100644 --- a/core/java/com/android/internal/widget/Magnifier.java +++ b/core/java/com/android/internal/widget/Magnifier.java @@ -63,7 +63,7 @@ public final class Magnifier { // the copy is finished. private final Handler mPixelCopyHandler = Handler.getMain(); // Current magnification scale. - private float mScale; + private final float mZoomScale; // Timer used to schedule the copy task. private Timer mTimer; @@ -76,11 +76,12 @@ public final class Magnifier { public Magnifier(@NonNull View view) { mView = Preconditions.checkNotNull(view); final Context context = mView.getContext(); + final float elevation = context.getResources().getDimension(R.dimen.magnifier_elevation); final View content = LayoutInflater.from(context).inflate(R.layout.magnifier, null); content.findViewById(R.id.magnifier_inner).setClipToOutline(true); mWindowWidth = context.getResources().getDimensionPixelSize(R.dimen.magnifier_width); mWindowHeight = context.getResources().getDimensionPixelSize(R.dimen.magnifier_height); - final float elevation = context.getResources().getDimension(R.dimen.magnifier_elevation); + mZoomScale = context.getResources().getFloat(R.dimen.magnifier_zoom_scale); mWindow = new PopupWindow(context); mWindow.setContentView(content); @@ -90,7 +91,9 @@ public final class Magnifier { mWindow.setTouchable(false); mWindow.setBackgroundDrawable(null); - mBitmap = Bitmap.createBitmap(mWindowWidth, mWindowHeight, Bitmap.Config.ARGB_8888); + final int bitmapWidth = (int) (mWindowWidth / mZoomScale); + final int bitmapHeight = (int) (mWindowHeight / mZoomScale); + mBitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888); getImageView().setImageBitmap(mBitmap); } @@ -101,20 +104,8 @@ public final class Magnifier { * to the view. The lower end is clamped to 0 * @param yPosInView vertical coordinate of the center point of the magnifier source * relative to the view. The lower end is clamped to 0 - * @param scale the scale at which the magnifier zooms on the source content. The - * lower end is clamped to 1 and the higher end to 4 */ - public void show(@FloatRange(from=0) float xPosInView, - @FloatRange(from=0) float yPosInView, - @FloatRange(from=1, to=4) float scale) { - if (scale > 4) { - scale = 4; - } - - if (scale < 1) { - scale = 1; - } - + public void show(@FloatRange(from=0) float xPosInView, @FloatRange(from=0) float yPosInView) { if (xPosInView < 0) { xPosInView = 0; } @@ -123,10 +114,6 @@ public final class Magnifier { yPosInView = 0; } - if (mScale != scale) { - resizeBitmap(scale); - } - mScale = scale; configureCoordinates(xPosInView, yPosInView); if (mTimer == null) { @@ -164,6 +151,7 @@ public final class Magnifier { /** * @return the height of the magnifier window. */ + @NonNull public int getHeight() { return mWindowHeight; } @@ -171,15 +159,17 @@ public final class Magnifier { /** * @return the width of the magnifier window. */ + @NonNull public int getWidth() { return mWindowWidth; } - private void resizeBitmap(float scale) { - final int bitmapWidth = (int) (mWindowWidth / scale); - final int bitmapHeight = (int) (mWindowHeight / scale); - mBitmap.reconfigure(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888); - getImageView().setImageBitmap(mBitmap); + /** + * @return the zoom scale of the magnifier. + */ + @NonNull + public float getZoomScale() { + return mZoomScale; } private void configureCoordinates(float xPosInView, float yPosInView) { diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp index fe14d483743f..7fd97b232f07 100644 --- a/core/jni/android_os_HwBinder.cpp +++ b/core/jni/android_os_HwBinder.cpp @@ -303,7 +303,8 @@ static jobject JHwBinder_native_getService( JNIEnv *env, jclass /* clazzObj */, jstring ifaceNameObj, - jstring serviceNameObj) { + jstring serviceNameObj, + jboolean isTry) { using ::android::hidl::base::V1_0::IBase; using ::android::hardware::details::getRawServiceInternal; @@ -331,8 +332,7 @@ static jobject JHwBinder_native_getService( std::string serviceName(serviceNameCStr); env->ReleaseStringUTFChars(serviceNameObj, serviceNameCStr); - // TODO(b/67981006): true /* retry */ - sp<IBase> ret = getRawServiceInternal(ifaceName, serviceName, false /* retry */, false /* getStub */); + sp<IBase> ret = getRawServiceInternal(ifaceName, serviceName, !isTry /* retry */, false /* getStub */); sp<hardware::IBinder> service = hardware::toBinder<hidl::base::V1_0::IBase>(ret); if (service == NULL) { @@ -371,7 +371,7 @@ static JNINativeMethod gMethods[] = { { "registerService", "(Ljava/lang/String;)V", (void *)JHwBinder_native_registerService }, - { "getService", "(Ljava/lang/String;Ljava/lang/String;)L" PACKAGE_PATH "/IHwBinder;", + { "getService", "(Ljava/lang/String;Ljava/lang/String;Z)L" PACKAGE_PATH "/IHwBinder;", (void *)JHwBinder_native_getService }, { "configureRpcThreadpool", "(JZ)V", diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp index 04e9dfd2706f..c1419ba6c7c6 100644 --- a/core/jni/android_text_StaticLayout.cpp +++ b/core/jni/android_text_StaticLayout.cpp @@ -55,11 +55,11 @@ static JLineBreaksID gLineBreaks_fieldID; class JNILineBreakerLineWidth : public minikin::LineBreaker::LineWidthDelegate { public: JNILineBreakerLineWidth(float firstWidth, int32_t firstLineCount, float restWidth, - std::vector<float>&& indents, std::vector<float>&& leftPaddings, - std::vector<float>&& rightPaddings, int32_t indentsAndPaddingsOffset) + const std::vector<float>& indents, const std::vector<float>& leftPaddings, + const std::vector<float>& rightPaddings, int32_t indentsAndPaddingsOffset) : mFirstWidth(firstWidth), mFirstLineCount(firstLineCount), mRestWidth(restWidth), - mIndents(std::move(indents)), mLeftPaddings(std::move(leftPaddings)), - mRightPaddings(std::move(rightPaddings)), mOffset(indentsAndPaddingsOffset) {} + mIndents(indents), mLeftPaddings(leftPaddings), + mRightPaddings(rightPaddings), mOffset(indentsAndPaddingsOffset) {} float getLineWidth(size_t lineNo) override { const float width = ((ssize_t)lineNo < (ssize_t)mFirstLineCount) @@ -91,9 +91,9 @@ class JNILineBreakerLineWidth : public minikin::LineBreaker::LineWidthDelegate { const float mFirstWidth; const int32_t mFirstLineCount; const float mRestWidth; - const std::vector<float> mIndents; - const std::vector<float> mLeftPaddings; - const std::vector<float> mRightPaddings; + const std::vector<float>& mIndents; + const std::vector<float>& mLeftPaddings; + const std::vector<float>& mRightPaddings; const int32_t mOffset; }; @@ -106,32 +106,132 @@ static inline std::vector<float> jintArrayToFloatVector(JNIEnv* env, jintArray j } } +class Run { + public: + Run(int32_t start, int32_t end) : mStart(start), mEnd(end) {} + virtual ~Run() {} + + virtual void addTo(minikin::LineBreaker* lineBreaker) = 0; + + protected: + const int32_t mStart; + const int32_t mEnd; + + private: + // Forbid copy and assign. + Run(const Run&) = delete; + void operator=(const Run&) = delete; +}; + +class StyleRun : public Run { + public: + StyleRun(int32_t start, int32_t end, minikin::MinikinPaint&& paint, + std::shared_ptr<minikin::FontCollection>&& collection, + minikin::FontStyle&& style, bool isRtl) + : Run(start, end), mPaint(std::move(paint)), mCollection(std::move(collection)), + mStyle(std::move(style)), mIsRtl(isRtl) {} + + void addTo(minikin::LineBreaker* lineBreaker) override { + lineBreaker->addStyleRun(&mPaint, mCollection, mStyle, mStart, mEnd, mIsRtl); + } + + private: + minikin::MinikinPaint mPaint; + std::shared_ptr<minikin::FontCollection> mCollection; + minikin::FontStyle mStyle; + const bool mIsRtl; +}; + +class Replacement : public Run { + public: + Replacement(int32_t start, int32_t end, float width, uint32_t localeListId) + : Run(start, end), mWidth(width), mLocaleListId(localeListId) {} + + void addTo(minikin::LineBreaker* lineBreaker) override { + lineBreaker->addReplacement(mStart, mEnd, mWidth, mLocaleListId); + } + + private: + const float mWidth; + const uint32_t mLocaleListId; +}; + +class StaticLayoutNative { + public: + StaticLayoutNative( + minikin::BreakStrategy strategy, minikin::HyphenationFrequency frequency, + bool isJustified, std::vector<float>&& indents, std::vector<float>&& leftPaddings, + std::vector<float>&& rightPaddings) + : mStrategy(strategy), mFrequency(frequency), mIsJustified(isJustified), + mIndents(std::move(indents)), mLeftPaddings(std::move(leftPaddings)), + mRightPaddings(std::move(rightPaddings)) {} + + void addStyleRun(int32_t start, int32_t end, minikin::MinikinPaint&& paint, + std::shared_ptr<minikin::FontCollection> collection, + minikin::FontStyle&& style, bool isRtl) { + mRuns.emplace_back(std::make_unique<StyleRun>( + start, end, std::move(paint), std::move(collection), std::move(style), isRtl)); + } + + void addReplacementRun(int32_t start, int32_t end, float width, uint32_t localeListId) { + mRuns.emplace_back(std::make_unique<Replacement>(start, end, width, localeListId)); + } + + // Only valid while this instance is alive. + inline std::unique_ptr<minikin::LineBreaker::LineWidthDelegate> buildLineWidthDelegate( + float firstWidth, int32_t firstLineCount, float restWidth, + int32_t indentsAndPaddingsOffset) { + return std::make_unique<JNILineBreakerLineWidth>( + firstWidth, firstLineCount, restWidth, mIndents, mLeftPaddings, mRightPaddings, + indentsAndPaddingsOffset); + } + + void addRuns(minikin::LineBreaker* lineBreaker) { + for (const auto& run : mRuns) { + run->addTo(lineBreaker); + } + } + + void clearRuns() { + mRuns.clear(); + } + + inline minikin::BreakStrategy getStrategy() const { return mStrategy; } + inline minikin::HyphenationFrequency getFrequency() const { return mFrequency; } + inline bool isJustified() const { return mIsJustified; } + + private: + const minikin::BreakStrategy mStrategy; + const minikin::HyphenationFrequency mFrequency; + const bool mIsJustified; + const std::vector<float> mIndents; + const std::vector<float> mLeftPaddings; + const std::vector<float> mRightPaddings; + + std::vector<std::unique_ptr<Run>> mRuns; +}; + +static inline StaticLayoutNative* toNative(jlong ptr) { + return reinterpret_cast<StaticLayoutNative*>(ptr); +} + // set text and set a number of parameters for creating a layout (width, tabstops, strategy, // hyphenFrequency) -static void nSetupParagraph(JNIEnv* env, jclass, jlong nativePtr, jcharArray text, jint length, - jfloat firstWidth, jint firstWidthLineLimit, jfloat restWidth, - jintArray variableTabStops, jint defaultTabStop, jint strategy, jint hyphenFrequency, - jboolean isJustified, jintArray indents, jintArray leftPaddings, jintArray rightPaddings, - jint indentsAndPaddingsOffset) { - minikin::LineBreaker* b = reinterpret_cast<minikin::LineBreaker*>(nativePtr); - b->resize(length); - env->GetCharArrayRegion(text, 0, length, b->buffer()); - b->setText(); - if (variableTabStops == nullptr) { - b->setTabStops(nullptr, 0, defaultTabStop); - } else { - ScopedIntArrayRO stops(env, variableTabStops); - b->setTabStops(stops.get(), stops.size(), defaultTabStop); - } - b->setStrategy(static_cast<minikin::BreakStrategy>(strategy)); - b->setHyphenationFrequency(static_cast<minikin::HyphenationFrequency>(hyphenFrequency)); - b->setJustified(isJustified); - - // TODO: copy indents and paddings only once when LineBreaker is started to be used. - b->setLineWidthDelegate(std::make_unique<JNILineBreakerLineWidth>( - firstWidth, firstWidthLineLimit, restWidth, jintArrayToFloatVector(env, indents), - jintArrayToFloatVector(env, leftPaddings), jintArrayToFloatVector(env, rightPaddings), - indentsAndPaddingsOffset)); +static jlong nInit(JNIEnv* env, jclass /* unused */, + jint breakStrategy, jint hyphenationFrequency, jboolean isJustified, + jintArray indents, jintArray leftPaddings, jintArray rightPaddings) { + return reinterpret_cast<jlong>(new StaticLayoutNative( + static_cast<minikin::BreakStrategy>(breakStrategy), + static_cast<minikin::HyphenationFrequency>(hyphenationFrequency), + isJustified, + jintArrayToFloatVector(env, indents), + jintArrayToFloatVector(env, leftPaddings), + jintArrayToFloatVector(env, rightPaddings))); +} + +// CriticalNative +static void nFinish(jlong nativePtr) { + delete toNative(nativePtr); } static void recycleCopy(JNIEnv* env, jobject recycle, jintArray recycleBreaks, @@ -163,42 +263,65 @@ static void recycleCopy(JNIEnv* env, jobject recycle, jintArray recycleBreaks, } static jint nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr, - jobject recycle, jintArray recycleBreaks, - jfloatArray recycleWidths, jfloatArray recycleAscents, - jfloatArray recycleDescents, jintArray recycleFlags, - jint recycleLength, jfloatArray charWidths) { - minikin::LineBreaker* b = reinterpret_cast<minikin::LineBreaker*>(nativePtr); + // Inputs + jcharArray text, + jint length, + jfloat firstWidth, + jint firstWidthLineCount, + jfloat restWidth, + jintArray variableTabStops, + jint defaultTabStop, + jint indentsOffset, + + // Outputs + jobject recycle, + jint recycleLength, + jintArray recycleBreaks, + jfloatArray recycleWidths, + jfloatArray recycleAscents, + jfloatArray recycleDescents, + jintArray recycleFlags, + jfloatArray charWidths) { + + StaticLayoutNative* builder = toNative(nativePtr); + + // TODO: Reorganize minikin APIs. + minikin::LineBreaker b; + b.resize(length); + env->GetCharArrayRegion(text, 0, length, b.buffer()); + b.setText(); + 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)); - size_t nBreaks = b->computeBreaks(); + builder->addRuns(&b); + + size_t nBreaks = b.computeBreaks(); recycleCopy(env, recycle, recycleBreaks, recycleWidths, recycleAscents, recycleDescents, - recycleFlags, recycleLength, nBreaks, b->getBreaks(), b->getWidths(), b->getAscents(), - b->getDescents(), b->getFlags()); + recycleFlags, recycleLength, nBreaks, b.getBreaks(), b.getWidths(), b.getAscents(), + b.getDescents(), b.getFlags()); - env->SetFloatArrayRegion(charWidths, 0, b->size(), b->charWidths()); + env->SetFloatArrayRegion(charWidths, 0, b.size(), b.charWidths()); - b->finish(); + b.finish(); + builder->clearRuns(); return static_cast<jint>(nBreaks); } -static jlong nNewBuilder(JNIEnv*, jclass) { - return reinterpret_cast<jlong>(new minikin::LineBreaker); -} - -static void nFreeBuilder(JNIEnv*, jclass, jlong nativePtr) { - delete reinterpret_cast<minikin::LineBreaker*>(nativePtr); -} - -static void nFinishBuilder(JNIEnv*, jclass, jlong nativePtr) { - minikin::LineBreaker* b = reinterpret_cast<minikin::LineBreaker*>(nativePtr); - b->finish(); -} - // Basically similar to Paint.getTextRunAdvances but with C++ interface -static void nAddStyleRun(JNIEnv* env, jclass, jlong nativePtr, jlong nativePaint, jint start, - jint end, jboolean isRtl) { - minikin::LineBreaker* b = reinterpret_cast<minikin::LineBreaker*>(nativePtr); +// CriticalNative +static void nAddStyleRun(jlong nativePtr, jlong nativePaint, jint start, jint end, jboolean isRtl) { + StaticLayoutNative* builder = toNative(nativePtr); Paint* paint = reinterpret_cast<Paint*>(nativePaint); const Typeface* typeface = paint->getAndroidTypeface(); minikin::MinikinPaint minikinPaint; @@ -206,26 +329,59 @@ static void nAddStyleRun(JNIEnv* env, jclass, jlong nativePtr, jlong nativePaint minikin::FontStyle style = MinikinUtils::prepareMinikinPaint(&minikinPaint, paint, typeface); - b->addStyleRun(&minikinPaint, resolvedTypeface->fFontCollection, style, start, end, isRtl); + builder->addStyleRun( + start, end, std::move(minikinPaint), resolvedTypeface->fFontCollection, std::move(style), + isRtl); } -static void nAddReplacementRun(JNIEnv* env, jclass, jlong nativePtr, jlong nativePaint, - jint start, jint end, jfloat width) { - minikin::LineBreaker* b = reinterpret_cast<minikin::LineBreaker*>(nativePtr); +// CriticalNative +static void nAddReplacementRun(jlong nativePtr, jlong nativePaint, jint start, jint end, + jfloat width) { + StaticLayoutNative* builder = toNative(nativePtr); Paint* paint = reinterpret_cast<Paint*>(nativePaint); - b->addReplacement(start, end, width, paint->getMinikinLangListId()); + builder->addReplacementRun(start, end, width, paint->getMinikinLangListId()); } static const JNINativeMethod gMethods[] = { - // TODO performance: many of these are candidates for fast jni, awaiting guidance - {"nNewBuilder", "()J", (void*) nNewBuilder}, - {"nFreeBuilder", "(J)V", (void*) nFreeBuilder}, - {"nFinishBuilder", "(J)V", (void*) nFinishBuilder}, - {"nSetupParagraph", "(J[CIFIF[IIIIZ[I[I[II)V", (void*) nSetupParagraph}, + // Fast Natives + {"nInit", "(" + "I" // breakStrategy + "I" // hyphenationFrequency + "Z" // isJustified + "[I" // indents + "[I" // left paddings + "[I" // right paddings + ")J", (void*) nInit}, + + // Critical Natives + {"nFinish", "(J)V", (void*) nFinish}, {"nAddStyleRun", "(JJIIZ)V", (void*) nAddStyleRun}, {"nAddReplacementRun", "(JJIIF)V", (void*) nAddReplacementRun}, - {"nComputeLineBreaks", "(JLandroid/text/StaticLayout$LineBreaks;[I[F[F[F[II[F)I", - (void*) nComputeLineBreaks} + + // Regular JNI + {"nComputeLineBreaks", "(" + "J" // nativePtr + + // Inputs + "[C" // text + "I" // length + "F" // firstWidth + "I" // firstWidthLineCount + "F" // restWidth + "[I" // variableTabStops + "I" // defaultTabStop + "I" // indentsOffset + + // Outputs + "Landroid/text/StaticLayout$LineBreaks;" // recycle + "I" // recycleLength + "[I" // recycleBreaks + "[F" // recycleWidths + "[F" // recycleAscents + "[F" // recycleDescents + "[I" // recycleFlags + "[F" // charWidths + ")I", (void*) nComputeLineBreaks} }; int register_android_text_StaticLayout(JNIEnv* env) diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 105bb7ef9f3f..507a431e8a28 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -183,15 +183,23 @@ <protected-broadcast android:name="android.bluetooth.a2dp-sink.profile.action.AUDIO_CONFIG_CHANGED" /> <protected-broadcast + android:name="android.bluetooth.avrcp-controller.profile.action.BROWSE_CONNECTION_STATE_CHANGED" /> + <protected-broadcast android:name="android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED" /> <protected-broadcast + android:name="android.bluetooth.avrcp-controller.profile.action.FOLDER_LIST" /> + <protected-broadcast + android:name="android.bluetooth.avrcp-controller.profile.action.TRACK_EVENT" /> + <protected-broadcast android:name="android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED" /> <protected-broadcast + android:name="android.bluetooth.input.profile.action.IDLE_TIME_CHANGED" /> + <protected-broadcast android:name="android.bluetooth.input.profile.action.PROTOCOL_MODE_CHANGED" /> <protected-broadcast android:name="android.bluetooth.input.profile.action.VIRTUAL_UNPLUG_STATUS" /> <protected-broadcast - android:name="android.bluetooth.inputhost.profile.action.CONNECTION_STATE_CHANGED" /> + android:name="android.bluetooth.hiddevice.profile.action.CONNECTION_STATE_CHANGED" /> <protected-broadcast android:name="android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED" /> <protected-broadcast android:name="android.bluetooth.mapmce.profile.action.CONNECTION_STATE_CHANGED" /> diff --git a/core/res/res/layout/magnifier.xml b/core/res/res/layout/magnifier.xml index d6cd8b4e2737..f3344c7470a1 100644 --- a/core/res/res/layout/magnifier.xml +++ b/core/res/res/layout/magnifier.xml @@ -22,10 +22,11 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/magnifier_inner" - android:layout_width="wrap_content" - android:layout_height="wrap_content" + android:layout_width="@android:dimen/magnifier_width" + android:layout_height="@android:dimen/magnifier_height" + android:elevation="@android:dimen/magnifier_elevation" android:background="?android:attr/floatingToolbarPopupBackgroundDrawable" - android:elevation="@android:dimen/magnifier_elevation"> + android:scaleType="fitXY"> <ImageView android:id="@+id/magnifier_image" android:layout_width="match_parent" diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 08e2233ebba7..dc75ba6077e7 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -525,6 +525,7 @@ <dimen name="magnifier_height">48dp</dimen> <dimen name="magnifier_elevation">2dp</dimen> <dimen name="magnifier_offset">42dp</dimen> + <item type="dimen" format="float" name="magnifier_zoom_scale">1.25</item> <dimen name="chooser_grid_padding">0dp</dimen> <!-- Spacing around the background change frome service to non-service --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 1627a0eca6dd..896de53cbc4e 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2484,6 +2484,7 @@ <java-symbol type="dimen" name="magnifier_width" /> <java-symbol type="dimen" name="magnifier_height" /> <java-symbol type="dimen" name="magnifier_elevation" /> + <java-symbol type="dimen" name="magnifier_zoom_scale" /> <java-symbol type="dimen" name="magnifier_offset" /> <java-symbol type="string" name="date_picker_prev_month_button" /> diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerStressTestRunner.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerStressTestRunner.java index fbaf0f324ab1..0806fa0b9879 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerStressTestRunner.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerStressTestRunner.java @@ -20,7 +20,6 @@ import android.os.Bundle; import android.test.InstrumentationTestRunner; import android.test.InstrumentationTestSuite; -import com.android.connectivitymanagertest.stress.WifiApStress; import com.android.connectivitymanagertest.stress.WifiStressTest; import junit.framework.TestSuite; @@ -35,7 +34,7 @@ import junit.framework.TestSuite; */ public class ConnectivityManagerStressTestRunner extends InstrumentationTestRunner { - private int mSoftApIterations = 100; + private int mSoftApIterations = 0; private int mScanIterations = 100; private int mReconnectIterations = 100; // sleep time before restart wifi, default is set to 2 minutes @@ -47,7 +46,6 @@ public class ConnectivityManagerStressTestRunner extends InstrumentationTestRunn @Override public TestSuite getAllTests() { TestSuite suite = new InstrumentationTestSuite(this); - suite.addTestSuite(WifiApStress.class); suite.addTestSuite(WifiStressTest.class); return suite; } @@ -60,13 +58,6 @@ public class ConnectivityManagerStressTestRunner extends InstrumentationTestRunn @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); - String valueStr = icicle.getString("softap_iterations"); - if (valueStr != null) { - int iteration = Integer.parseInt(valueStr); - if (iteration > 0) { - mSoftApIterations = iteration; - } - } String scanIterationStr = icicle.getString("scan_iterations"); if (scanIterationStr != null) { diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java index 64fed7f77176..3706e4b3d8e8 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java @@ -129,12 +129,6 @@ public class ConnectivityManagerTestBase extends InstrumentationTestCase { // Get an instance of WifiManager mWifiManager =(WifiManager)mContext.getSystemService(Context.WIFI_SERVICE); - if (mWifiManager.isWifiApEnabled()) { - // if soft AP is enabled, disable it - mWifiManager.setWifiApEnabled(null, false); - logv("Disable soft ap"); - } - // register a connectivity receiver for CONNECTIVITY_ACTION; mConnectivityReceiver = new ConnectivityReceiver(); mContext.registerReceiver(mConnectivityReceiver, diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerUnitTestRunner.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerUnitTestRunner.java index 0e57a006eed0..746cb841da66 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerUnitTestRunner.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerUnitTestRunner.java @@ -19,7 +19,6 @@ package com.android.connectivitymanagertest; import android.test.InstrumentationTestRunner; import android.test.InstrumentationTestSuite; import com.android.connectivitymanagertest.unit.WifiClientTest; -import com.android.connectivitymanagertest.unit.WifiSoftAPTest; import junit.framework.TestSuite; @@ -35,7 +34,6 @@ public class ConnectivityManagerUnitTestRunner extends InstrumentationTestRunner public TestSuite getAllTests() { TestSuite suite = new InstrumentationTestSuite(this); suite.addTestSuite(WifiClientTest.class); - suite.addTestSuite(WifiSoftAPTest.class); return suite; } diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java deleted file mode 100644 index de934b9c9d82..000000000000 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2010, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.connectivitymanagertest.stress; - - -import android.net.wifi.WifiConfiguration; -import android.net.wifi.WifiConfiguration.AuthAlgorithm; -import android.net.wifi.WifiConfiguration.KeyMgmt; -import android.net.wifi.WifiManager; -import android.os.Environment; -import android.test.suitebuilder.annotation.LargeTest; - -import com.android.connectivitymanagertest.ConnectivityManagerStressTestRunner; -import com.android.connectivitymanagertest.ConnectivityManagerTestBase; - -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; - -/** - * Stress test setting up device as wifi hotspot - */ -public class WifiApStress extends ConnectivityManagerTestBase { - private static String NETWORK_ID = "AndroidAPTest"; - private static String PASSWD = "androidwifi"; - private final static String OUTPUT_FILE = "WifiStressTestOutput.txt"; - private int mTotalIterations; - private BufferedWriter mOutputWriter = null; - private int mLastIteration = 0; - private boolean mWifiOnlyFlag; - - public WifiApStress() { - super(WifiApStress.class.getSimpleName()); - } - - @Override - protected void setUp() throws Exception { - super.setUp(); - ConnectivityManagerStressTestRunner mRunner = - (ConnectivityManagerStressTestRunner)getInstrumentation(); - mTotalIterations = mRunner.getSoftApInterations(); - mWifiOnlyFlag = mRunner.isWifiOnly(); - turnScreenOn(); - } - - @Override - protected void tearDown() throws Exception { - // write the total number of iterations into output file - mOutputWriter = new BufferedWriter(new FileWriter(new File( - Environment.getExternalStorageDirectory(), OUTPUT_FILE))); - mOutputWriter.write(String.format("iteration %d out of %d\n", - mLastIteration + 1, mTotalIterations)); - mOutputWriter.flush(); - mOutputWriter.close(); - super.tearDown(); - } - - @LargeTest - public void testWifiHotSpot() { - if (mWifiOnlyFlag) { - logv(getName() + " is excluded for wi-fi only test"); - return; - } - WifiConfiguration config = new WifiConfiguration(); - config.SSID = NETWORK_ID; - config.allowedKeyManagement.set(KeyMgmt.WPA_PSK); - config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN); - config.preSharedKey = PASSWD; - - // if wifiap enabled, disable it - assertTrue("failed to disable wifi hotspot", - mWifiManager.setWifiApEnabled(config, false)); - assertTrue("wifi hotspot not enabled", waitForWifiApState( - WifiManager.WIFI_AP_STATE_DISABLED, 2 * LONG_TIMEOUT)); - - // if Wifi is enabled, disable it - if (mWifiManager.isWifiEnabled()) { - assertTrue("failed to disable wifi", disableWifi()); - // wait for the wifi state to be DISABLED - assertTrue("wifi state not disabled", waitForWifiState( - WifiManager.WIFI_STATE_DISABLED, LONG_TIMEOUT)); - } - int i; - for (i = 0; i < mTotalIterations; i++) { - logv("iteration: " + i); - mLastIteration = i; - // enable Wifi tethering - assertTrue("failed to enable wifi hotspot", - mWifiManager.setWifiApEnabled(config, true)); - // wait for wifi ap state to be ENABLED - assertTrue("wifi hotspot not enabled", waitForWifiApState( - WifiManager.WIFI_AP_STATE_ENABLED, 2 * LONG_TIMEOUT)); - // wait for wifi tethering result - assertTrue("tether state not changed", waitForTetherStateChange(LONG_TIMEOUT)); - // allow the wifi tethering to be enabled for 10 seconds - try { - Thread.sleep(2 * SHORT_TIMEOUT); - } catch (Exception e) { - // ignore - } - assertTrue("no uplink data connection after Wi-Fi tethering", pingTest()); - // disable wifi hotspot - assertTrue("failed to disable wifi hotspot", - mWifiManager.setWifiApEnabled(config, false)); - assertTrue("wifi hotspot not enabled", waitForWifiApState( - WifiManager.WIFI_AP_STATE_DISABLED, 2 * LONG_TIMEOUT)); - assertFalse("wifi hotspot still enabled", mWifiManager.isWifiApEnabled()); - } - } - -} diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiSoftAPTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiSoftAPTest.java deleted file mode 100644 index f202862f6dbf..000000000000 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiSoftAPTest.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.connectivitymanagertest.unit; - -import android.content.Context; -import android.net.wifi.WifiManager; -import android.net.wifi.WifiConfiguration; -import android.net.wifi.WifiConfiguration.KeyMgmt; - -import android.test.suitebuilder.annotation.LargeTest; -import android.test.AndroidTestCase; - -import android.util.Log; - -/** - * Test Wifi soft AP configuration - */ -public class WifiSoftAPTest extends AndroidTestCase { - - private WifiManager mWifiManager; - private WifiConfiguration mWifiConfig = null; - private final String TAG = "WifiSoftAPTest"; - private final int DURATION = 10000; - - @Override - protected void setUp() throws Exception { - super.setUp(); - mWifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE); - assertNotNull(mWifiManager); - assertTrue(mWifiManager.setWifiApEnabled(null, true)); - mWifiConfig = mWifiManager.getWifiApConfiguration(); - if (mWifiConfig != null) { - Log.v(TAG, "mWifiConfig is " + mWifiConfig.toString()); - } else { - Log.v(TAG, "mWifiConfig is null."); - } - } - - @Override - protected void tearDown() throws Exception { - Log.v(TAG, "turn off wifi tethering"); - mWifiManager.setWifiApEnabled(null, false); - super.tearDown(); - } - - // Test case 1: Test the soft AP SSID with letters - @LargeTest - public void testApSsidWithAlphabet() { - WifiConfiguration config = new WifiConfiguration(); - config.SSID = "abcdefghijklmnopqrstuvwxyz"; - config.allowedKeyManagement.set(KeyMgmt.NONE); - mWifiConfig = config; - assertTrue(mWifiManager.setWifiApEnabled(mWifiConfig, true)); - try { - Thread.sleep(DURATION); - } catch (InterruptedException e) { - Log.v(TAG, "exception " + e.getStackTrace()); - assertFalse(true); - } - assertNotNull(mWifiManager.getWifiApConfiguration()); - assertEquals("wifi AP state is not enabled", WifiManager.WIFI_AP_STATE_ENABLED, - mWifiManager.getWifiApState()); - } -} diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java index 31ce95eea1d2..4b32ceae0617 100644 --- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java +++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java @@ -256,13 +256,13 @@ public class BluetoothStressTest extends InstrumentationTestCase { mTestUtils.unpair(mAdapter, device); mTestUtils.pair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey, BluetoothTestRunner.sDevicePairPin); - mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.INPUT_DEVICE, null); + mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.HID_HOST, null); for (int i = 0; i < iterations; i++) { mTestUtils.writeOutput("connectInput iteration " + (i + 1) + " of " + iterations); - mTestUtils.connectProfile(mAdapter, device, BluetoothProfile.INPUT_DEVICE, + mTestUtils.connectProfile(mAdapter, device, BluetoothProfile.HID_HOST, String.format("connectInput(device=%s)", device)); - mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.INPUT_DEVICE, + mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.HID_HOST, String.format("disconnectInput(device=%s)", device)); } diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java index ee159788ad21..ada03666b7ba 100644 --- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java +++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java @@ -227,8 +227,8 @@ public class BluetoothTestUtils extends Assert { case BluetoothProfile.HEADSET: mConnectionAction = BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED; break; - case BluetoothProfile.INPUT_DEVICE: - mConnectionAction = BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED; + case BluetoothProfile.HID_HOST: + mConnectionAction = BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED; break; case BluetoothProfile.PAN: mConnectionAction = BluetoothPan.ACTION_CONNECTION_STATE_CHANGED; @@ -322,8 +322,8 @@ public class BluetoothTestUtils extends Assert { case BluetoothProfile.HEADSET: mHeadset = (BluetoothHeadset) proxy; break; - case BluetoothProfile.INPUT_DEVICE: - mInput = (BluetoothInputDevice) proxy; + case BluetoothProfile.HID_HOST: + mInput = (BluetoothHidHost) proxy; break; case BluetoothProfile.PAN: mPan = (BluetoothPan) proxy; @@ -342,7 +342,7 @@ public class BluetoothTestUtils extends Assert { case BluetoothProfile.HEADSET: mHeadset = null; break; - case BluetoothProfile.INPUT_DEVICE: + case BluetoothProfile.HID_HOST: mInput = null; break; case BluetoothProfile.PAN: @@ -362,7 +362,7 @@ public class BluetoothTestUtils extends Assert { private Context mContext; private BluetoothA2dp mA2dp = null; private BluetoothHeadset mHeadset = null; - private BluetoothInputDevice mInput = null; + private BluetoothHidHost mInput = null; private BluetoothPan mPan = null; /** @@ -894,7 +894,7 @@ public class BluetoothTestUtils extends Assert { * @param adapter The BT adapter. * @param device The remote device. * @param profile The profile to connect. One of {@link BluetoothProfile#A2DP}, - * {@link BluetoothProfile#HEADSET}, or {@link BluetoothProfile#INPUT_DEVICE}. + * {@link BluetoothProfile#HEADSET}, or {@link BluetoothProfile#HID_HOST}. * @param methodName The method name to printed in the logs. If null, will be * "connectProfile(profile=<profile>, device=<device>)" */ @@ -935,8 +935,8 @@ public class BluetoothTestUtils extends Assert { assertTrue(((BluetoothA2dp)proxy).connect(device)); } else if (profile == BluetoothProfile.HEADSET) { assertTrue(((BluetoothHeadset)proxy).connect(device)); - } else if (profile == BluetoothProfile.INPUT_DEVICE) { - assertTrue(((BluetoothInputDevice)proxy).connect(device)); + } else if (profile == BluetoothProfile.HID_HOST) { + assertTrue(((BluetoothHidHost)proxy).connect(device)); } break; default: @@ -975,7 +975,7 @@ public class BluetoothTestUtils extends Assert { * @param adapter The BT adapter. * @param device The remote device. * @param profile The profile to disconnect. One of {@link BluetoothProfile#A2DP}, - * {@link BluetoothProfile#HEADSET}, or {@link BluetoothProfile#INPUT_DEVICE}. + * {@link BluetoothProfile#HEADSET}, or {@link BluetoothProfile#HID_HOST}. * @param methodName The method name to printed in the logs. If null, will be * "connectProfile(profile=<profile>, device=<device>)" */ @@ -1010,8 +1010,8 @@ public class BluetoothTestUtils extends Assert { assertTrue(((BluetoothA2dp)proxy).disconnect(device)); } else if (profile == BluetoothProfile.HEADSET) { assertTrue(((BluetoothHeadset)proxy).disconnect(device)); - } else if (profile == BluetoothProfile.INPUT_DEVICE) { - assertTrue(((BluetoothInputDevice)proxy).disconnect(device)); + } else if (profile == BluetoothProfile.HID_HOST) { + assertTrue(((BluetoothHidHost)proxy).disconnect(device)); } break; case BluetoothProfile.STATE_DISCONNECTED: @@ -1237,7 +1237,7 @@ public class BluetoothTestUtils extends Assert { long s = System.currentTimeMillis(); while (System.currentTimeMillis() - s < CONNECT_DISCONNECT_PROFILE_TIMEOUT) { state = mPan.getConnectionState(device); - if (state == BluetoothInputDevice.STATE_DISCONNECTED + if (state == BluetoothHidHost.STATE_DISCONNECTED && (receiver.getFiredFlags() & mask) == mask) { long finish = receiver.getCompletedTime(); if (start != -1 && finish != -1) { @@ -1255,7 +1255,7 @@ public class BluetoothTestUtils extends Assert { int firedFlags = receiver.getFiredFlags(); removeReceiver(receiver); fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%s)", - methodName, state, BluetoothInputDevice.STATE_DISCONNECTED, firedFlags, mask)); + methodName, state, BluetoothHidHost.STATE_DISCONNECTED, firedFlags, mask)); } /** @@ -1404,7 +1404,7 @@ public class BluetoothTestUtils extends Assert { String[] actions = { BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED, BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED, - BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED}; + BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED}; ConnectProfileReceiver receiver = new ConnectProfileReceiver(device, profile, expectedFlags); addReceiver(receiver, actions); @@ -1443,7 +1443,7 @@ public class BluetoothTestUtils extends Assert { return mHeadset; } break; - case BluetoothProfile.INPUT_DEVICE: + case BluetoothProfile.HID_HOST: if (mInput != null) { return mInput; } @@ -1469,7 +1469,7 @@ public class BluetoothTestUtils extends Assert { sleep(POLL_TIME); } return mHeadset; - case BluetoothProfile.INPUT_DEVICE: + case BluetoothProfile.HID_HOST: while (mInput == null && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) { sleep(POLL_TIME); } diff --git a/core/tests/coretests/src/android/text/TextLineTest.java b/core/tests/coretests/src/android/text/TextLineTest.java new file mode 100644 index 000000000000..f8d688498b5f --- /dev/null +++ b/core/tests/coretests/src/android/text/TextLineTest.java @@ -0,0 +1,67 @@ +/* + * 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.text; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.support.test.filters.SmallTest; +import android.support.test.filters.Suppress; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class TextLineTest { + private boolean stretchesToFullWidth(CharSequence line) { + final TextPaint paint = new TextPaint(); + final TextLine tl = TextLine.obtain(); + tl.set(paint, line, 0, line.length(), Layout.DIR_LEFT_TO_RIGHT, + Layout.DIRS_ALL_LEFT_TO_RIGHT, false /* hasTabs */, null /* tabStops */); + final float originalWidth = tl.metrics(null); + final float expandedWidth = 2 * originalWidth; + + tl.justify(expandedWidth); + final float newWidth = tl.metrics(null); + TextLine.recycle(tl); + return Math.abs(newWidth - expandedWidth) < 0.5; + } + + @Test + public void testJustify_spaces() { + // There are no spaces to stretch. + assertFalse(stretchesToFullWidth("text")); + + assertTrue(stretchesToFullWidth("one space")); + assertTrue(stretchesToFullWidth("exactly two spaces")); + assertTrue(stretchesToFullWidth("up to three spaces")); + } + + // NBSP should also stretch when it's not used as a base for a combining mark. This doesn't work + // yet (b/68204709). + @Suppress + public void disabledTestJustify_NBSP() { + final char nbsp = '\u00A0'; + assertTrue(stretchesToFullWidth("non-breaking" + nbsp + "space")); + assertTrue(stretchesToFullWidth("mix" + nbsp + "and match")); + + final char combining_acute = '\u0301'; + assertFalse(stretchesToFullWidth("combining" + nbsp + combining_acute + "acute")); + } +} diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 6f2b03838b06..2f9ae578ade1 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -24,6 +24,10 @@ applications that come with the platform <permission name="android.permission.PROVIDE_RESOLVER_RANKER_SERVICE" /> </privapp-permissions> + <privapp-permissions package="com.android.apps.tag"> + <permission name="android.permission.WRITE_SECURE_SETTINGS"/> + </privapp-permissions> + <privapp-permissions package="com.android.backupconfirm"> <permission name="android.permission.BACKUP"/> <permission name="android.permission.CRYPT_KEEPER"/> @@ -54,6 +58,7 @@ applications that come with the platform <permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/> <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/> <permission name="android.permission.MODIFY_PHONE_STATE"/> + <permission name="android.permission.STATUS_BAR"/> <permission name="android.permission.STOP_APP_SWITCHES"/> <permission name="com.android.voicemail.permission.READ_VOICEMAIL"/> <permission name="com.android.voicemail.permission.WRITE_VOICEMAIL"/> @@ -353,6 +358,7 @@ applications that come with the platform <permission name="android.permission.WRITE_DREAM_STATE"/> <permission name="android.permission.WRITE_MEDIA_STORAGE"/> <permission name="android.permission.WRITE_SECURE_SETTINGS"/> + <permission name="com.android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"/> </privapp-permissions> <privapp-permissions package="com.android.tv"> diff --git a/libs/protoutil/include/android/util/ProtoOutputStream.h b/libs/protoutil/include/android/util/ProtoOutputStream.h index 0f1ccedeaae2..10be6499417f 100644 --- a/libs/protoutil/include/android/util/ProtoOutputStream.h +++ b/libs/protoutil/include/android/util/ProtoOutputStream.h @@ -49,7 +49,7 @@ public: bool write(uint64_t fieldId, long long val); bool write(uint64_t fieldId, bool val); bool write(uint64_t fieldId, std::string val); - bool write(uint64_t fieldId, const char* val); + bool write(uint64_t fieldId, const char* val, size_t size); /** * Starts a sub-message write session. @@ -103,4 +103,4 @@ private: } } -#endif // ANDROID_UTIL_PROTOOUTPUT_STREAM_H
\ No newline at end of file +#endif // ANDROID_UTIL_PROTOOUTPUT_STREAM_H diff --git a/libs/protoutil/src/ProtoOutputStream.cpp b/libs/protoutil/src/ProtoOutputStream.cpp index 15144ac2eb28..9dadf1c20510 100644 --- a/libs/protoutil/src/ProtoOutputStream.cpp +++ b/libs/protoutil/src/ProtoOutputStream.cpp @@ -225,14 +225,13 @@ ProtoOutputStream::write(uint64_t fieldId, string val) } bool -ProtoOutputStream::write(uint64_t fieldId, const char* val) +ProtoOutputStream::write(uint64_t fieldId, const char* val, size_t size) { if (mCompact) return false; const uint32_t id = (uint32_t)fieldId; - int size = 0; - while (val[size] != '\0') size++; switch (fieldId & FIELD_TYPE_MASK) { case TYPE_STRING: + case TYPE_BYTES: writeUtf8StringImpl(id, val, size); return true; default: diff --git a/packages/EasterEgg/src/com/android/egg/octo/Ocquarium.java b/packages/EasterEgg/src/com/android/egg/octo/Ocquarium.java index bbbdf80612d7..8a06cc604f1e 100644 --- a/packages/EasterEgg/src/com/android/egg/octo/Ocquarium.java +++ b/packages/EasterEgg/src/com/android/egg/octo/Ocquarium.java @@ -26,6 +26,7 @@ import com.android.egg.R; public class Ocquarium extends Activity { ImageView mImageView; + private OctopusDrawable mOcto; @Override protected void onCreate(Bundle savedInstanceState) { @@ -43,10 +44,9 @@ public class Ocquarium extends Activity { bg.addView(mImageView, new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); - final OctopusDrawable octo = new OctopusDrawable(getApplicationContext()); - octo.setSizePx((int) (OctopusDrawable.randfrange(40f,180f) * dp)); - mImageView.setImageDrawable(octo); - octo.startDrift(); + mOcto = new OctopusDrawable(getApplicationContext()); + mOcto.setSizePx((int) (OctopusDrawable.randfrange(40f,180f) * dp)); + mImageView.setImageDrawable(mOcto); mImageView.setOnTouchListener(new View.OnTouchListener() { boolean touching; @@ -54,24 +54,36 @@ public class Ocquarium extends Activity { public boolean onTouch(View view, MotionEvent motionEvent) { switch (motionEvent.getActionMasked()) { case MotionEvent.ACTION_DOWN: - if (octo.hitTest(motionEvent.getX(), motionEvent.getY())) { + if (mOcto.hitTest(motionEvent.getX(), motionEvent.getY())) { touching = true; - octo.stopDrift(); + mOcto.stopDrift(); } break; case MotionEvent.ACTION_MOVE: if (touching) { - octo.moveTo(motionEvent.getX(), motionEvent.getY()); + mOcto.moveTo(motionEvent.getX(), motionEvent.getY()); } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: touching = false; - octo.startDrift(); + mOcto.startDrift(); break; } return true; } }); } + + @Override + protected void onPause() { + mOcto.stopDrift(); + super.onPause(); + } + + @Override + protected void onResume() { + super.onResume(); + mOcto.startDrift(); + } } diff --git a/packages/SettingsLib/OWNERS b/packages/SettingsLib/OWNERS new file mode 100644 index 000000000000..4211c272b774 --- /dev/null +++ b/packages/SettingsLib/OWNERS @@ -0,0 +1,21 @@ +# People who can approve changes for submission +asapperstein@google.com +asargent@google.com +dehboxturtle@google.com +dhnishi@google.com +dling@google.com +dsandler@google.com +evanlaird@google.com +jackqdyulei@google.com +jmonk@google.com +mfritze@google.com +nicoya@google.com +rogerxue@google.com +virgild@google.com +zhfan@google.com + +# Emergency approvers in case the above are not available +miket@google.com + +# Exempt resource files (because they are in a flat directory and too hard to manage via OWNERS) +per-file *.xml=*
\ No newline at end of file diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java index 8def03648558..d90386f904ec 100644 --- a/packages/SettingsLib/src/com/android/settingslib/Utils.java +++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java @@ -12,9 +12,7 @@ import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import android.graphics.Color; -import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; import android.net.ConnectivityManager; import android.os.BatteryManager; @@ -297,4 +295,9 @@ public class Utils { } return defaultDays; } + + public static boolean isWifiOnly(Context context) { + return !context.getSystemService(ConnectivityManager.class) + .isNetworkSupported(ConnectivityManager.TYPE_MOBILE); + } } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java index d1621da09a6c..213002fb9726 100755 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java @@ -19,7 +19,7 @@ package com.android.settingslib.bluetooth; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothInputDevice; +import android.bluetooth.BluetoothHidHost; import android.bluetooth.BluetoothProfile; import android.content.Context; import android.util.Log; @@ -35,7 +35,7 @@ public class HidProfile implements LocalBluetoothProfile { private static final String TAG = "HidProfile"; private static boolean V = true; - private BluetoothInputDevice mService; + private BluetoothHidHost mService; private boolean mIsProfileReady; private final LocalBluetoothAdapter mLocalAdapter; @@ -53,7 +53,7 @@ public class HidProfile implements LocalBluetoothProfile { public void onServiceConnected(int profile, BluetoothProfile proxy) { if (V) Log.d(TAG,"Bluetooth service connected"); - mService = (BluetoothInputDevice) proxy; + mService = (BluetoothHidHost) proxy; // We just bound to the service, so refresh the UI for any connected HID devices. List<BluetoothDevice> deviceList = mService.getConnectedDevices(); while (!deviceList.isEmpty()) { @@ -87,7 +87,7 @@ public class HidProfile implements LocalBluetoothProfile { mDeviceManager = deviceManager; mProfileManager = profileManager; adapter.getProfileProxy(context, new InputDeviceServiceListener(), - BluetoothProfile.INPUT_DEVICE); + BluetoothProfile.HID_HOST); } public boolean isConnectable() { @@ -190,7 +190,7 @@ public class HidProfile implements LocalBluetoothProfile { if (V) Log.d(TAG, "finalize()"); if (mService != null) { try { - BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.INPUT_DEVICE, + BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.HID_HOST, mService); mService = null; }catch (Throwable t) { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java index 0750dc750051..9cda669379dd 100755 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java @@ -21,9 +21,9 @@ import android.bluetooth.BluetoothA2dpSink; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothHeadsetClient; +import android.bluetooth.BluetoothHidHost; import android.bluetooth.BluetoothMap; import android.bluetooth.BluetoothMapClient; -import android.bluetooth.BluetoothInputDevice; import android.bluetooth.BluetoothPan; import android.bluetooth.BluetoothPbapClient; import android.bluetooth.BluetoothProfile; @@ -123,7 +123,7 @@ public class LocalBluetoothProfileManager { // Always add HID and PAN profiles mHidProfile = new HidProfile(context, mLocalAdapter, mDeviceManager, this); addProfile(mHidProfile, HidProfile.NAME, - BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED); + BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED); mPanProfile = new PanProfile(context); addPanProfile(mPanProfile, PanProfile.NAME, diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSimStatusImeiInfoPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSimStatusImeiInfoPreferenceController.java new file mode 100644 index 000000000000..a78440c271c9 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSimStatusImeiInfoPreferenceController.java @@ -0,0 +1,36 @@ +/* + * 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.settingslib.deviceinfo; + +import android.content.Context; +import android.os.UserManager; + +import com.android.settingslib.Utils; +import com.android.settingslib.core.AbstractPreferenceController; + +public abstract class AbstractSimStatusImeiInfoPreferenceController + extends AbstractPreferenceController { + public AbstractSimStatusImeiInfoPreferenceController(Context context) { + super(context); + } + + @Override + public boolean isAvailable() { + return mContext.getSystemService(UserManager.class).isAdminUser() + && !Utils.isWifiOnly(mContext); + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java b/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java index 817989ab96ae..f4c9bb31f01a 100755 --- a/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java +++ b/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java @@ -415,8 +415,8 @@ public class BatteryMeterDrawableBase extends Drawable { : (mLevel == 100 ? 0.38f : 0.5f))); mTextHeight = -mTextPaint.getFontMetrics().ascent; pctText = String.valueOf(SINGLE_DIGIT_PERCENT ? (level / 10) : level); - pctX = mWidth * 0.5f; - pctY = (mHeight + mTextHeight) * 0.47f; + pctX = mWidth * 0.5f + left; + pctY = (mHeight + mTextHeight) * 0.47f + top; pctOpaque = levelTop > pctY; if (!pctOpaque) { mTextPath.reset(); @@ -439,8 +439,8 @@ public class BatteryMeterDrawableBase extends Drawable { if (!mCharging && !mPowerSaveEnabled) { if (level <= mCriticalLevel) { // draw the warning text - final float x = mWidth * 0.5f; - final float y = (mHeight + mWarningTextHeight) * 0.48f; + final float x = mWidth * 0.5f + left; + final float y = (mHeight + mWarningTextHeight) * 0.48f + top; c.drawText(mWarningString, x, y, mWarningTextPaint); } else if (pctOpaque) { // draw the percentage text diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/OWNERS b/packages/SettingsLib/src/com/android/settingslib/inputmethod/OWNERS new file mode 100644 index 000000000000..a0e28baee3f6 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/OWNERS @@ -0,0 +1,5 @@ +# Default reviewers for this and subdirectories. +takaoka@google.com +yukawa@google.com + +# Emergency approvers in case the above are not available
\ No newline at end of file diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java new file mode 100644 index 000000000000..28409fae45e6 --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java @@ -0,0 +1,120 @@ +/* + * 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.settingslib.deviceinfo; + +import static com.google.common.truth.Truth.assertThat; + +import static org.robolectric.shadow.api.Shadow.extract; + +import android.net.ConnectivityManager; +import android.os.UserManager; +import android.util.SparseBooleanArray; + +import com.android.settingslib.SettingsLibRobolectricTestRunner; +import com.android.settingslib.TestConfig; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; + +@RunWith(SettingsLibRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION, + shadows = {SimStatusImeiInfoPreferenceControllerTest.ShadowUserManager.class, + SimStatusImeiInfoPreferenceControllerTest.ShadowConnectivityManager.class}) +public class SimStatusImeiInfoPreferenceControllerTest { + + private AbstractSimStatusImeiInfoPreferenceController mController; + + @Before + public void setUp() { + mController = new AbstractSimStatusImeiInfoPreferenceController( + RuntimeEnvironment.application) { + @Override + public String getPreferenceKey() { + return null; + } + }; + } + + @Test + public void testIsAvailable_isAdminAndHasMobile_shouldReturnTrue() { + ShadowUserManager userManager = + extract(RuntimeEnvironment.application.getSystemService(UserManager.class)); + userManager.setIsAdminUser(true); + ShadowConnectivityManager connectivityManager = + extract(RuntimeEnvironment.application.getSystemService(ConnectivityManager.class)); + connectivityManager.setNetworkSupported(ConnectivityManager.TYPE_MOBILE, true); + + assertThat(mController.isAvailable()).isTrue(); + } + + @Test + public void testIsAvailable_isAdminButNoMobile_shouldReturnFalse() { + ShadowUserManager userManager = + extract(RuntimeEnvironment.application.getSystemService(UserManager.class)); + userManager.setIsAdminUser(true); + ShadowConnectivityManager connectivityManager = + extract(RuntimeEnvironment.application.getSystemService(ConnectivityManager.class)); + connectivityManager.setNetworkSupported(ConnectivityManager.TYPE_MOBILE, false); + + assertThat(mController.isAvailable()).isFalse(); + } + + @Test + public void testIsAvailable_isNotAdmin_shouldReturnFalse() { + ShadowUserManager userManager = + extract(RuntimeEnvironment.application.getSystemService(UserManager.class)); + userManager.setIsAdminUser(false); + + assertThat(mController.isAvailable()).isFalse(); + } + + @Implements(UserManager.class) + public static class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager { + + private boolean mAdminUser; + + public void setIsAdminUser(boolean isAdminUser) { + mAdminUser = isAdminUser; + } + + @Implementation + public boolean isAdminUser() { + return mAdminUser; + } + } + + @Implements(ConnectivityManager.class) + public static class ShadowConnectivityManager + extends org.robolectric.shadows.ShadowConnectivityManager { + + private final SparseBooleanArray mSupportedNetworkTypes = new SparseBooleanArray(); + + public void setNetworkSupported(int networkType, boolean supported) { + mSupportedNetworkTypes.put(networkType, supported); + } + + @Implementation + public boolean isNetworkSupported(int networkType) { + return mSupportedNetworkTypes.get(networkType); + } + } +} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java new file mode 100644 index 000000000000..3522b8abfe62 --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java @@ -0,0 +1,85 @@ +/* + * 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.settingslib.graph; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.verify; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; + +import com.android.settingslib.SettingsLibRobolectricTestRunner; +import com.android.settingslib.TestConfig; +import com.android.settingslib.testutils.shadow.SettingsLibShadowResources; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +@RunWith(SettingsLibRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION, + shadows = SettingsLibShadowResources.class) +public class BatteryMeterDrawableBaseTest { + private static final int PADDING = 5; + private static final int HEIGHT = 80; + private static final int WIDTH = 40; + @Mock + private Canvas mCanvas; + private Context mContext; + private BatteryMeterDrawableBase mBatteryMeterDrawableBase; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mContext = RuntimeEnvironment.application; + mBatteryMeterDrawableBase = new BatteryMeterDrawableBase(mContext, 0 /* frameColor */); + } + + @Test + public void testDraw_hasPaddingAndBounds_drawWarningInCorrectPosition() { + mBatteryMeterDrawableBase.setPadding(PADDING, PADDING, PADDING, PADDING); + mBatteryMeterDrawableBase.setBounds(0, 0, WIDTH + 2 * PADDING, HEIGHT + 2 * PADDING); + mBatteryMeterDrawableBase.setBatteryLevel(3); + + mBatteryMeterDrawableBase.draw(mCanvas); + + // WIDTH * 0.5 + PADDING = 25 + // (HEIGHT + TEXT_HEIGHT) * 0.48 + PADDING = 43.3999998 + verify(mCanvas).drawText(eq("!"), eq(25f), eq(43.399998f), any(Paint.class)); + } + + @Test + public void testDraw_hasPaddingAndBounds_drawBatteryLevelInCorrectPosition() { + mBatteryMeterDrawableBase.setPadding(PADDING, PADDING, PADDING, PADDING); + mBatteryMeterDrawableBase.setBounds(0, 0, WIDTH + 2 * PADDING, HEIGHT + 2 * PADDING); + mBatteryMeterDrawableBase.setBatteryLevel(20); + mBatteryMeterDrawableBase.setShowPercent(true); + + mBatteryMeterDrawableBase.draw(mCanvas); + + // WIDTH * 0.5 + PADDING = 25 + // (HEIGHT + TEXT_HEIGHT) * 0.47 + PADDING = 42.6 + verify(mCanvas).drawText(eq("20"), eq(25f), eq(42.6f), any(Paint.class)); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index d24e51c73151..40ee8386e895 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -382,13 +382,6 @@ public class NetworkControllerImpl extends BroadcastReceiver new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... args) { - // Disable tethering if enabling Wifi - final int wifiApState = mWifiManager.getWifiApState(); - if (enabled && ((wifiApState == WifiManager.WIFI_AP_STATE_ENABLING) || - (wifiApState == WifiManager.WIFI_AP_STATE_ENABLED))) { - mWifiManager.setWifiApEnabled(null, false); - } - mWifiManager.setWifiEnabled(enabled); return null; } diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java index cf1d33c86173..a139ac4f3f68 100644 --- a/services/core/java/com/android/server/IpSecService.java +++ b/services/core/java/com/android/server/IpSecService.java @@ -754,7 +754,7 @@ public class IpSecService extends IIpSecService.Stub { * and re-binding, during which the system could *technically* hand that port out to someone * else. */ - private void bindToRandomPort(FileDescriptor sockFd) throws IOException { + private int bindToRandomPort(FileDescriptor sockFd) throws IOException { for (int i = MAX_PORT_BIND_ATTEMPTS; i > 0; i--) { try { FileDescriptor probeSocket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); @@ -763,7 +763,7 @@ public class IpSecService extends IIpSecService.Stub { Os.close(probeSocket); Log.v(TAG, "Binding to port " + port); Os.bind(sockFd, INADDR_ANY, port); - return; + return port; } catch (ErrnoException e) { // Someone miraculously claimed the port just after we closed probeSocket. if (e.errno == OsConstants.EADDRINUSE) { @@ -803,7 +803,7 @@ public class IpSecService extends IIpSecService.Stub { Log.v(TAG, "Binding to port " + port); Os.bind(sockFd, INADDR_ANY, port); } else { - bindToRandomPort(sockFd); + port = bindToRandomPort(sockFd); } // This code is common to both the unspecified and specified port cases Os.setsockoptInt( diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index cd256109e1bd..bfbce408cb28 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -641,8 +641,8 @@ class StorageManagerService extends IStorageManager.Stub implements Watchdog.Mon break; } case H_PARTITION_FORGET: { - final String partGuid = (String) msg.obj; - forgetPartition(partGuid); + final VolumeRecord rec = (VolumeRecord) msg.obj; + forgetPartition(rec.partGuid, rec.fsUuid); break; } case H_RESET: { @@ -1694,7 +1694,7 @@ class StorageManagerService extends IStorageManager.Stub implements Watchdog.Mon synchronized (mLock) { final VolumeRecord rec = mRecords.remove(fsUuid); if (rec != null && !TextUtils.isEmpty(rec.partGuid)) { - mHandler.obtainMessage(H_PARTITION_FORGET, rec.partGuid).sendToTarget(); + mHandler.obtainMessage(H_PARTITION_FORGET, rec).sendToTarget(); } mCallbacks.notifyVolumeForgotten(fsUuid); @@ -1718,7 +1718,7 @@ class StorageManagerService extends IStorageManager.Stub implements Watchdog.Mon final String fsUuid = mRecords.keyAt(i); final VolumeRecord rec = mRecords.valueAt(i); if (!TextUtils.isEmpty(rec.partGuid)) { - mHandler.obtainMessage(H_PARTITION_FORGET, rec.partGuid).sendToTarget(); + mHandler.obtainMessage(H_PARTITION_FORGET, rec).sendToTarget(); } mCallbacks.notifyVolumeForgotten(fsUuid); } @@ -1733,9 +1733,9 @@ class StorageManagerService extends IStorageManager.Stub implements Watchdog.Mon } } - private void forgetPartition(String partGuid) { + private void forgetPartition(String partGuid, String fsUuid) { try { - mVold.forgetPartition(partGuid); + mVold.forgetPartition(partGuid, fsUuid); } catch (Exception e) { Slog.wtf(TAG, e); } diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java index 089db871aa2f..29073cb68c04 100644 --- a/services/core/java/com/android/server/am/ActivityDisplay.java +++ b/services/core/java/com/android/server/am/ActivityDisplay.java @@ -38,7 +38,9 @@ import static com.android.server.am.proto.ActivityDisplayProto.CONFIGURATION_CON import static com.android.server.am.proto.ActivityDisplayProto.STACKS; import static com.android.server.am.proto.ActivityDisplayProto.ID; +import android.annotation.Nullable; import android.app.ActivityManagerInternal; +import android.app.ActivityOptions; import android.app.WindowConfiguration; import android.util.IntArray; import android.util.Slog; @@ -206,6 +208,18 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> { } /** + * Returns an existing stack compatible with the input params or creates one + * if a compatible stack doesn't exist. + * @see #getOrCreateStack(int, int, boolean) + */ + <T extends ActivityStack> T getOrCreateStack(@Nullable ActivityRecord r, + @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, int activityType, + boolean onTop) { + final int windowingMode = resolveWindowingMode(r, options, candidateTask, activityType); + return getOrCreateStack(windowingMode, activityType, onTop); + } + + /** * Creates a stack matching the input windowing mode and activity type on this display. * @param windowingMode The windowing mode the stack should be created in. If * {@link WindowConfiguration#WINDOWING_MODE_UNDEFINED} then the stack will @@ -235,7 +249,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> { } final ActivityManagerService service = mSupervisor.mService; - if (!mSupervisor.isWindowingModeSupported(windowingMode, service.mSupportsMultiWindow, + if (!isWindowingModeSupported(windowingMode, service.mSupportsMultiWindow, service.mSupportsSplitScreenMultiWindow, service.mSupportsFreeformWindowManagement, service.mSupportsPictureInPicture, activityType)) { throw new IllegalArgumentException("Can't create stack for unsupported windowingMode=" @@ -252,35 +266,29 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> { } } - windowingMode = updateWindowingModeForSplitScreenIfNeeded(windowingMode, activityType); - final int stackId = mSupervisor.getNextStackId(); + return createStackUnchecked(windowingMode, activityType, stackId, onTop); + } - final T stack = createStackUnchecked(windowingMode, activityType, stackId, onTop); + @VisibleForTesting + <T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType, + int stackId, boolean onTop) { + if (windowingMode == WINDOWING_MODE_PINNED) { + return (T) new PinnedActivityStack(this, stackId, mSupervisor, onTop); + } + final T stack = (T) new ActivityStack( + this, stackId, mSupervisor, windowingMode, activityType, onTop); if (mDisplayId == DEFAULT_DISPLAY && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { - // Make sure recents stack exist when creating a dock stack as it normally need to be on - // the other side of the docked stack and we make visibility decisions based on that. + // 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 on that. // TODO: Not sure if this is needed after we change to calculate visibility based on // stack z-order vs. id. getOrCreateStack(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_RECENTS, onTop); } - return stack; } - @VisibleForTesting - <T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType, - int stackId, boolean onTop) { - switch (windowingMode) { - case WINDOWING_MODE_PINNED: - return (T) new PinnedActivityStack(this, stackId, mSupervisor, onTop); - default: - return (T) new ActivityStack( - this, stackId, mSupervisor, windowingMode, activityType, onTop); - } - } - /** * Removes stacks in the input windowing modes from the system if they are of activity type * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED @@ -372,6 +380,105 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> { } } + /** + * Returns true if the {@param windowingMode} is supported based on other parameters passed in. + * @param windowingMode The windowing mode we are checking support for. + * @param supportsMultiWindow If we should consider support for multi-window mode in general. + * @param supportsSplitScreen If we should consider support for split-screen multi-window. + * @param supportsFreeform If we should consider support for freeform multi-window. + * @param supportsPip If we should consider support for picture-in-picture mutli-window. + * @param activityType The activity type under consideration. + * @return true if the windowing mode is supported. + */ + private boolean isWindowingModeSupported(int windowingMode, boolean supportsMultiWindow, + boolean supportsSplitScreen, boolean supportsFreeform, boolean supportsPip, + int activityType) { + + if (windowingMode == WINDOWING_MODE_UNDEFINED + || windowingMode == WINDOWING_MODE_FULLSCREEN) { + return true; + } + if (!supportsMultiWindow) { + return false; + } + + if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY + || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) { + return supportsSplitScreen && WindowConfiguration.supportSplitScreenWindowingMode( + windowingMode, activityType); + } + + if (!supportsFreeform && windowingMode == WINDOWING_MODE_FREEFORM) { + return false; + } + + if (!supportsPip && windowingMode == WINDOWING_MODE_PINNED) { + return false; + } + return true; + } + + int resolveWindowingMode(@Nullable ActivityRecord r, @Nullable ActivityOptions options, + @Nullable TaskRecord task, int activityType) { + + // First preference if the windowing mode in the activity options if set. + int windowingMode = (options != null) + ? options.getLaunchWindowingMode() : WINDOWING_MODE_UNDEFINED; + + // If windowing mode is unset, then next preference is the candidate task, then the + // activity record. + if (windowingMode == WINDOWING_MODE_UNDEFINED) { + if (task != null) { + windowingMode = task.getWindowingMode(); + } + if (windowingMode == WINDOWING_MODE_UNDEFINED && r != null) { + windowingMode = r.getWindowingMode(); + } + if (windowingMode == WINDOWING_MODE_UNDEFINED) { + // Use the display's windowing mode. + windowingMode = getWindowingMode(); + } + } + + // Make sure the windowing mode we are trying to use makes sense for what is supported. + final ActivityManagerService service = mSupervisor.mService; + boolean supportsMultiWindow = service.mSupportsMultiWindow; + boolean supportsSplitScreen = service.mSupportsSplitScreenMultiWindow; + boolean supportsFreeform = service.mSupportsFreeformWindowManagement; + boolean supportsPip = service.mSupportsPictureInPicture; + if (supportsMultiWindow) { + if (task != null) { + supportsMultiWindow = task.isResizeable(); + supportsSplitScreen = task.supportsSplitScreenWindowingMode(); + // TODO: Do we need to check for freeform and Pip support here? + } else if (r != null) { + supportsMultiWindow = r.isResizeable(); + supportsSplitScreen = r.supportsSplitScreenWindowingMode(); + supportsFreeform = r.supportsFreeform(); + supportsPip = r.supportsPictureInPicture(); + } + } + + final boolean inSplitScreenMode = hasSplitScreenPrimaryStack(); + if (!inSplitScreenMode + && windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY) { + // Switch to fullscreen windowing mode if we are not in split-screen mode and we are + // trying to launch in split-screen secondary. + windowingMode = WINDOWING_MODE_FULLSCREEN; + } else if (inSplitScreenMode && windowingMode == WINDOWING_MODE_FULLSCREEN + && supportsSplitScreen) { + windowingMode = WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; + } + + if (windowingMode != WINDOWING_MODE_UNDEFINED + && isWindowingModeSupported(windowingMode, supportsMultiWindow, supportsSplitScreen, + supportsFreeform, supportsPip, activityType)) { + return windowingMode; + } + // Return the display's windowing mode + return getWindowingMode(); + } + /** Returns the top visible stack activity type that isn't in the exclude windowing mode. */ int getTopVisibleStackActivityType(int excludeWindowingMode) { for (int i = mStacks.size() - 1; i >= 0; --i) { @@ -408,6 +515,14 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> { } } + /** We are in the process of exiting split-screen mode. */ + void onExitingSplitScreenMode() { + // Remove reference to the primary-split-screen stack so it no longer has any effect on the + // display. For example, we want to be able to create fullscreen stack for standard activity + // types when exiting split-screen mode. + mSplitScreenPrimaryStack = null; + } + ActivityStack getSplitScreenPrimaryStack() { return mSplitScreenPrimaryStack; } @@ -424,21 +539,6 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> { return mPinnedStack != null; } - int updateWindowingModeForSplitScreenIfNeeded(int windowingMode, int activityType) { - final boolean inSplitScreenMode = hasSplitScreenPrimaryStack(); - if (!inSplitScreenMode - && windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY) { - // Switch to fullscreen windowing mode if we are not in split-screen mode and we are - // trying to launch in split-screen secondary. - return WINDOWING_MODE_FULLSCREEN; - } else if (inSplitScreenMode && windowingMode == WINDOWING_MODE_FULLSCREEN - && WindowConfiguration.supportSplitScreenWindowingMode( - windowingMode, activityType)) { - return WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; - } - return windowingMode; - } - @Override public String toString() { return "ActivityDisplay={" + mDisplayId + " numStacks=" + mStacks.size() + "}"; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index e1e53b3b0b31..3fd1f4a63747 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -31,6 +31,7 @@ import static android.app.ActivityManager.StackId.INVALID_STACK_ID; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS; @@ -10318,7 +10319,8 @@ public class ActivityManagerService extends IActivityManager.Stub } // TODO(multi-display): Have the caller pass in the windowing mode and activity type. final ActivityStack stack = display.createStack( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /*onTop*/); + WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, + ON_TOP); return (stack != null) ? stack.mStackId : INVALID_STACK_ID; } } @@ -14239,12 +14241,12 @@ public class ActivityManagerService extends IActivityManager.Stub } } sb.append("\n"); - if (info.crashInfo != null && info.crashInfo.stackTrace != null) { - sb.append(info.crashInfo.stackTrace); + if (info.hasStackTrace()) { + sb.append(info.getStackTrace()); sb.append("\n"); } - if (info.message != null) { - sb.append(info.message); + if (info.getViolationDetails() != null) { + sb.append(info.getViolationDetails()); sb.append("\n"); } diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 2c72a4db635a..b885eabae57c 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -165,6 +165,7 @@ import android.view.IAppTransitionAnimationSpecsFuture; import android.view.IApplicationToken; import android.view.WindowManager.LayoutParams; +import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.ResolverActivity; import com.android.internal.content.ReferrerIntent; @@ -232,7 +233,9 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo final String processName; // process where this component wants to run final String taskAffinity; // as per ActivityInfo.taskAffinity final boolean stateNotNeeded; // As per ActivityInfo.flags - boolean fullscreen; // covers the full screen? + boolean fullscreen; // The activity is opaque and fills the entire space of this task. + // TODO: See if it possible to combine this with the fullscreen field. + final boolean hasWallpaper; // Has a wallpaper window as a background. final boolean noDisplay; // activity is not displayed? private final boolean componentSpecified; // did caller specify an explicit component? final boolean rootVoiceInteraction; // was this the root activity of a voice interaction? @@ -883,9 +886,14 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo Entry ent = AttributeCache.instance().get(packageName, realTheme, com.android.internal.R.styleable.Window, userId); - fullscreen = ent != null && !ActivityInfo.isTranslucentOrFloating(ent.array); - noDisplay = ent != null && ent.array.getBoolean( - com.android.internal.R.styleable.Window_windowNoDisplay, false); + if (ent != null) { + fullscreen = !ActivityInfo.isTranslucentOrFloating(ent.array); + hasWallpaper = ent.array.getBoolean(R.styleable.Window_windowShowWallpaper, false); + noDisplay = ent.array.getBoolean(R.styleable.Window_windowNoDisplay, false); + } else { + hasWallpaper = false; + noDisplay = false; + } setActivityType(_componentSpecified, _launchedFromUid, _intent, options, sourceRecord); diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 941c371b717d..f05243b1563a 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -489,22 +489,19 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai activityType = ACTIVITY_TYPE_STANDARD; } final ActivityDisplay display = getDisplay(); - if (display != null) { - if (activityType == ACTIVITY_TYPE_STANDARD + if (display != null && activityType == ACTIVITY_TYPE_STANDARD && windowingMode == WINDOWING_MODE_UNDEFINED) { - // Standard activity types will mostly take on the windowing mode of the display if - // one isn't specified, so look-up a compatible stack based on the display's - // windowing mode. - windowingMode = display.getWindowingMode(); - } - windowingMode = - display.updateWindowingModeForSplitScreenIfNeeded(windowingMode, activityType); + // Standard activity types will mostly take on the windowing mode of the display if one + // isn't specified, so look-up a compatible stack based on the display's windowing mode. + windowingMode = display.getWindowingMode(); } return super.isCompatible(windowingMode, activityType); } /** Adds the stack to specified display and calls WindowManager to do the same. */ void reparent(ActivityDisplay activityDisplay, boolean onTop) { + // TODO: We should probably resolve the windowing mode for the stack on the new display here + // so that it end up in a compatible mode in the new display. e.g. split-screen secondary. removeFromDisplay(); mTmpRect2.setEmpty(); postAddToDisplay(activityDisplay, mTmpRect2.isEmpty() ? null : mTmpRect2, onTop); @@ -1496,25 +1493,19 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } } - /** Returns true if the stack contains a fullscreen task. */ - private boolean hasFullscreenTask() { - for (int i = mTaskHistory.size() - 1; i >= 0; --i) { - final TaskRecord task = mTaskHistory.get(i); - if (task.mFullscreen) { - return true; - } - } - return false; - } - /** * Returns true if the stack is translucent and can have other contents visible behind it if * needed. A stack is considered translucent if it don't contain a visible or * starting (about to be visible) activity that is fullscreen (opaque). * @param starting The currently starting activity or null if there is none. + * TODO: Can be removed once we are no longer using returnToType for back functionality * @param stackBehind The stack directly behind this one. */ - private boolean isStackTranslucent(ActivityRecord starting, ActivityStack stackBehind) { + @VisibleForTesting + boolean isStackTranslucent(ActivityRecord starting, ActivityStack stackBehind) { + if (!isAttached() || mForceHidden) { + return true; + } for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { final TaskRecord task = mTaskHistory.get(taskNdx); final ArrayList<ActivityRecord> activities = task.mActivities; @@ -1533,7 +1524,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai continue; } - if (r.fullscreen) { + if (r.fullscreen || r.hasWallpaper) { // Stack isn't translucent if it has at least one fullscreen activity // that is visible. return false; @@ -1572,129 +1563,68 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (!isAttached() || mForceHidden) { return false; } - - final ActivityDisplay display = getDisplay(); - if (isTopStackOnDisplay() || mStackSupervisor.isFocusedStack(this)) { - return true; - } - - final int stackIndex = display.getIndexOf(this); - - // Check position and visibility of this stack relative to the front stack on its display. - final ActivityStack topStack = getDisplay().getTopStack(); - final int windowingMode = getWindowingMode(); - final int activityType = getActivityType(); - - if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { - // If the assistant stack is focused and translucent, then the docked stack is always - // visible - if (topStack.isActivityTypeAssistant()) { - return topStack.isStackTranslucent(starting, this); - } + if (mStackSupervisor.isFocusedStack(this)) { return true; } - // Set home stack to invisible when it is below but not immediately below the docked stack - // A case would be if recents stack exists but has no tasks and is below the docked stack - // and home stack is below recents - if (activityType == ACTIVITY_TYPE_HOME) { - final ActivityStack splitScreenStack = display.getSplitScreenPrimaryStack(); - int dockedStackIndex = display.getIndexOf(splitScreenStack); - if (dockedStackIndex > stackIndex && stackIndex != dockedStackIndex - 1) { - return false; - } - } - - // Find the first stack behind front stack that actually got something visible. - int stackBehindTopIndex = display.getIndexOf(topStack) - 1; - while (stackBehindTopIndex >= 0 && - display.getChildAt(stackBehindTopIndex).topRunningActivityLocked() == null) { - stackBehindTopIndex--; - } - final ActivityStack stackBehindTop = (stackBehindTopIndex >= 0) - ? display.getChildAt(stackBehindTopIndex) : null; - int stackBehindTopWindowingMode = WINDOWING_MODE_UNDEFINED; - int stackBehindTopActivityType = ACTIVITY_TYPE_UNDEFINED; - if (stackBehindTop != null) { - stackBehindTopWindowingMode = stackBehindTop.getWindowingMode(); - stackBehindTopActivityType = stackBehindTop.getActivityType(); - } - - final boolean alwaysOnTop = topStack.getWindowConfiguration().isAlwaysOnTop(); - if (topStack.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY || alwaysOnTop) { - if (this == stackBehindTop) { - // Stacks directly behind the docked or pinned stack are always visible. - return true; - } else if (alwaysOnTop && stackIndex == stackBehindTopIndex - 1) { - // Otherwise, this stack can also be visible if it is directly behind a docked stack - // or translucent assistant stack behind an always-on-top top-most stack - if (stackBehindTopWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { - return true; - } else if (stackBehindTopActivityType == ACTIVITY_TYPE_ASSISTANT) { - return stackBehindTop.isStackTranslucent(starting, this); - } - } + final ActivityRecord top = topRunningActivityLocked(); + if (top == null && isInStackLocked(starting) == null && !isTopStackOnDisplay()) { + // Shouldn't be visible if you don't have any running activities, not starting one, and + // not the top stack on display. + return false; } - if (topStack.isBackdropToTranslucentActivity() - && topStack.isStackTranslucent(starting, stackBehindTop)) { - // Stacks behind the fullscreen or assistant stack with a translucent activity are - // always visible so they can act as a backdrop to the translucent activity. - // For example, dialog activities - if (stackIndex == stackBehindTopIndex) { + final ActivityDisplay display = getDisplay(); + boolean gotOpaqueSplitScreenPrimary = false; + boolean gotOpaqueSplitScreenSecondary = false; + final int windowingMode = getWindowingMode(); + for (int i = display.getChildCount() - 1; i >= 0; --i) { + final ActivityStack other = display.getChildAt(i); + if (other == this) { + // Should be visible if there is no other stack occluding it. return true; } - if (stackBehindTopIndex >= 0) { - if ((stackBehindTopWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY - || stackBehindTopWindowingMode == WINDOWING_MODE_PINNED) - && stackIndex == (stackBehindTopIndex - 1)) { - // The stack behind the docked or pinned stack is also visible so we can have a - // complete backdrop to the translucent activity when the docked stack is up. - return true; - } - } - } - - if (isOnHomeDisplay()) { - // Visibility of any stack on default display should have been determined by the - // conditions above. - return false; - } - final int stackCount = display.getChildCount(); - for (int i = stackIndex + 1; i < stackCount; i++) { - final ActivityStack stack = display.getChildAt(i); + final int otherWindowingMode = other.getWindowingMode(); + // TODO: Can be removed once we are no longer using returnToType for back functionality + final ActivityStack stackBehind = i > 0 ? display.getChildAt(i - 1) : null; - if (!stack.mFullscreen && !stack.hasFullscreenTask()) { - continue; - } - - if (!stack.isDynamicStacksVisibleBehindAllowed()) { - // These stacks can't have any dynamic stacks visible behind them. + if (otherWindowingMode == WINDOWING_MODE_FULLSCREEN) { + if (other.isStackTranslucent(starting, stackBehind)) { + // Can be visible behind a translucent fullscreen stack. + continue; + } return false; + } else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY + && !gotOpaqueSplitScreenPrimary) { + gotOpaqueSplitScreenPrimary = + !other.isStackTranslucent(starting, stackBehind); + if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY + && gotOpaqueSplitScreenPrimary) { + // Can not be visible behind another opaque stack in split-screen-primary mode. + return false; + } + } else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY + && !gotOpaqueSplitScreenSecondary) { + gotOpaqueSplitScreenSecondary = + !other.isStackTranslucent(starting, stackBehind); + if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY + && gotOpaqueSplitScreenSecondary) { + // Can not be visible behind another opaque stack in split-screen-secondary mode. + return false; + } } - - if (!stack.isStackTranslucent(starting, null /* stackBehind */)) { + if (gotOpaqueSplitScreenPrimary && gotOpaqueSplitScreenSecondary) { + // Can not be visible if we are in split-screen windowing mode and both halves of + // the screen are opaque. return false; } } + // Well, nothing is stopping you from being visible... return true; } - private boolean isBackdropToTranslucentActivity() { - if (isActivityTypeAssistant()) { - return true; - } - final int windowingMode = getWindowingMode(); - return windowingMode == WINDOWING_MODE_FULLSCREEN - || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; - } - - private boolean isDynamicStacksVisibleBehindAllowed() { - return isActivityTypeAssistant() || getWindowingMode() == WINDOWING_MODE_PINNED; - } - final int rankTaskLayers(int baseLayer) { int layer = 0; for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { @@ -5263,7 +5193,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai public String toString() { return "ActivityStack{" + Integer.toHexString(System.identityHashCode(this)) + " stackId=" + mStackId + " type=" + activityTypeToString(getActivityType()) - + " mode=" + windowingModeToString(getWindowingMode()) + ", " + + " mode=" + windowingModeToString(getWindowingMode()) + + " visible=" + shouldBeVisible(null /* starting */) + ", " + mTaskHistory.size() + " tasks}"; } diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index c15b5e2e9bbd..7a4a0d4f23b3 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -110,7 +110,6 @@ import android.app.AppOpsManager; import android.app.ProfilerInfo; import android.app.ResultInfo; import android.app.WaitResult; -import android.app.WindowConfiguration; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -223,13 +222,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // at the top of its container (e.g. stack). static final boolean ON_TOP = true; - // Used to indicate that an objects (e.g. task) removal from its container - // (e.g. stack) is due to it moving to another container. - static final boolean MOVING = true; - - // Force the focus to change to the stack we are moving a task to.. - static final boolean FORCE_FOCUS = true; - // Don't execute any calls to resume. static final boolean DEFER_RESUME = true; @@ -405,6 +397,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D * object each time. */ private final Rect tempRect = new Rect(); + private final ActivityOptions mTmpOptions = ActivityOptions.makeBasic(); // The default minimal size that will be used if the activity doesn't specify its minimal size. // It will be calculated when the default display gets added. @@ -2186,89 +2179,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return null; } - /** - * Returns true if the {@param windowingMode} is supported based on other parameters passed in. - * @param windowingMode The windowing mode we are checking support for. - * @param supportsMultiWindow If we should consider support for multi-window mode in general. - * @param supportsSplitScreen If we should consider support for split-screen multi-window. - * @param supportsFreeform If we should consider support for freeform multi-window. - * @param supportsPip If we should consider support for picture-in-picture mutli-window. - * @param activityType The activity type under consideration. - * @return true if the windowing mode is supported. - */ - boolean isWindowingModeSupported(int windowingMode, boolean supportsMultiWindow, - boolean supportsSplitScreen, boolean supportsFreeform, boolean supportsPip, - int activityType) { - - if (windowingMode == WINDOWING_MODE_UNDEFINED - || windowingMode == WINDOWING_MODE_FULLSCREEN) { - return true; - } - if (!supportsMultiWindow) { - return false; - } - - if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY - || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) { - return supportsSplitScreen && WindowConfiguration.supportSplitScreenWindowingMode( - windowingMode, activityType); - } - - if (!supportsFreeform && windowingMode == WINDOWING_MODE_FREEFORM) { - return false; - } - - if (!supportsPip && windowingMode == WINDOWING_MODE_PINNED) { - return false; - } - return true; - } - - private int resolveWindowingMode(@Nullable ActivityRecord r, @Nullable ActivityOptions options, - @Nullable TaskRecord task, int activityType) { - - // First preference if the windowing mode in the activity options if set. - int windowingMode = (options != null) - ? options.getLaunchWindowingMode() : WINDOWING_MODE_UNDEFINED; - - // If windowing mode is unset, then next preference is the candidate task, then the - // activity record. - if (windowingMode == WINDOWING_MODE_UNDEFINED) { - if (task != null) { - windowingMode = task.getWindowingMode(); - } - if (windowingMode == WINDOWING_MODE_UNDEFINED && r != null) { - windowingMode = r.getWindowingMode(); - } - } - - // Make sure the windowing mode we are trying to use makes sense for what is supported. - boolean supportsMultiWindow = mService.mSupportsMultiWindow; - boolean supportsSplitScreen = mService.mSupportsSplitScreenMultiWindow; - boolean supportsFreeform = mService.mSupportsFreeformWindowManagement; - boolean supportsPip = mService.mSupportsPictureInPicture; - if (supportsMultiWindow) { - if (task != null) { - supportsMultiWindow = task.isResizeable(); - supportsSplitScreen = task.supportsSplitScreenWindowingMode(); - // TODO: Do we need to check for freeform and Pip support here? - } else if (r != null) { - supportsMultiWindow = r.isResizeable(); - supportsSplitScreen = r.supportsSplitScreenWindowingMode(); - supportsFreeform = r.supportsFreeform(); - supportsPip = r.supportsPictureInPicture(); - } - } - - if (windowingMode != WINDOWING_MODE_UNDEFINED - && isWindowingModeSupported(windowingMode, supportsMultiWindow, supportsSplitScreen, - supportsFreeform, supportsPip, activityType)) { - return windowingMode; - } - // Return root/systems windowing mode - return getWindowingMode(); - } - int resolveActivityType(@Nullable ActivityRecord r, @Nullable ActivityOptions options, @Nullable TaskRecord task) { // Preference is given to the activity type for the activity then the task since the type @@ -2329,7 +2239,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } final int activityType = resolveActivityType(r, options, candidateTask); - int windowingMode = resolveWindowingMode(r, options, candidateTask, activityType); T stack = null; // Next preference for stack goes to the display Id set in the activity options or the @@ -2347,7 +2256,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } final ActivityDisplay display = getActivityDisplayOrCreateLocked(displayId); if (display != null) { - stack = display.getOrCreateStack(windowingMode, activityType, onTop); + stack = display.getOrCreateStack(r, options, candidateTask, activityType, onTop); if (stack != null) { return stack; } @@ -2365,10 +2274,14 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D stack = r.getStack(); } if (stack != null) { - if (stack.isCompatible(windowingMode, activityType)) { - return stack; - } display = stack.getDisplay(); + if (display != null) { + final int windowingMode = + display.resolveWindowingMode(r, options, candidateTask, activityType); + if (stack.isCompatible(windowingMode, activityType)) { + return stack; + } + } } if (display == null @@ -2379,7 +2292,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D display = getDefaultDisplay(); } - return display.getOrCreateStack(windowingMode, activityType, onTop); + return display.getOrCreateStack(r, options, candidateTask, activityType, onTop); } /** @@ -2596,6 +2509,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D final ActivityDisplay toDisplay = getActivityDisplay(toDisplayId); if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { + // Tell the display we are exiting split-screen mode. + toDisplay.onExitingSplitScreenMode(); // We are moving all tasks from the docked stack to the fullscreen stack, // which is dismissing the docked stack, so resize all other stacks to // fullscreen here already so we don't end up with resize trashing. @@ -2625,35 +2540,34 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D final ArrayList<TaskRecord> tasks = fromStack.getAllTasks(); if (!tasks.isEmpty()) { + mTmpOptions.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN); final int size = tasks.size(); - final ActivityStack fullscreenStack = toDisplay.getOrCreateStack( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, onTop); - - if (onTop) { - final int returnToType = - toDisplay.getTopVisibleStackActivityType(WINDOWING_MODE_PINNED); - for (int i = 0; i < size; i++) { - final TaskRecord task = tasks.get(i); + for (int i = 0; i < size; ++i) { + final TaskRecord task = tasks.get(i); + final ActivityStack toStack = toDisplay.getOrCreateStack( + null, mTmpOptions, task, task.getActivityType(), onTop); + + if (onTop) { + final int returnToType = + toDisplay.getTopVisibleStackActivityType(WINDOWING_MODE_PINNED); final boolean isTopTask = i == (size - 1); if (inPinnedWindowingMode) { - // Update the return-to to reflect where the pinned stack task was moved - // from so that we retain the stack that was previously visible if the - // pinned stack is recreated. See moveActivityToPinnedStackLocked(). + // Update the return-to to reflect where the pinned stack task was + // moved from so that we retain the stack that was previously + // visible if the pinned stack is recreated. + // See moveActivityToPinnedStackLocked(). task.setTaskToReturnTo(returnToType); } // Defer resume until all the tasks have been moved to the fullscreen stack - task.reparent(fullscreenStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, + task.reparent(toStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, isTopTask /* animate */, DEFER_RESUME, schedulePictureInPictureModeChange, "moveTasksToFullscreenStack - onTop"); - } - } else { - for (int i = 0; i < size; i++) { - final TaskRecord task = tasks.get(i); + } else { // Position the tasks in the fullscreen stack in order at the bottom of the // stack. Also defer resume until all the tasks have been moved to the // fullscreen stack. - task.reparent(fullscreenStack, i /* position */, + task.reparent(toStack, ON_TOP, REPARENT_LEAVE_STACK_IN_PLACE, !ANIMATE, DEFER_RESUME, schedulePictureInPictureModeChange, "moveTasksToFullscreenStack - NOT_onTop"); @@ -3080,23 +2994,16 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D + " task=" + task); } - // We don't allow moving a unresizeable task to the docked stack since the docked stack is - // used for split-screen mode and will cause things like the docked divider to show up. We - // instead leave the task in its current stack or move it to the fullscreen stack if it - // isn't currently in a stack. + // Leave the task in its current stack or a fullscreen stack if it isn't resizeable and the + // preferred stack is in multi-window mode. if (inMultiWindowMode && !task.isResizeable()) { - Slog.w(TAG, "Can not move unresizeable task=" + task + " to docked stack." - + " Moving to stackId=" + stackId + " instead."); - // Temporarily disable resizeablility of the task as we don't want it to be resized if, - // for example, a docked stack is created which will lead to the stack we are moving - // from being resized and and its resizeable tasks being resized. - try { - task.mTemporarilyUnresizable = true; - stack = stack.getDisplay().createStack( - WINDOWING_MODE_FULLSCREEN, stack.getActivityType(), toTop); - } finally { - task.mTemporarilyUnresizable = false; + Slog.w(TAG, "Can not move unresizeable task=" + task + " to multi-window stack=" + stack + + " Moving to a fullscreen stack instead."); + if (prevStack != null) { + return prevStack; } + stack = stack.getDisplay().createStack( + WINDOWING_MODE_FULLSCREEN, stack.getActivityType(), toTop); } return stack; } @@ -4287,9 +4194,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D final boolean isSecondaryDisplayPreferred = (preferredDisplayId != DEFAULT_DISPLAY && preferredDisplayId != INVALID_DISPLAY); final boolean inSplitScreenMode = actualStack != null - && actualStack.inSplitScreenWindowingMode(); + && actualStack.getDisplay().hasSplitScreenPrimaryStack(); if (((!inSplitScreenMode && preferredWindowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) - && !isSecondaryDisplayPreferred) || task.isActivityTypeHome()) { + && !isSecondaryDisplayPreferred) || !task.isActivityTypeStandardOrUndefined()) { return; } diff --git a/services/core/java/com/android/server/am/AppTaskImpl.java b/services/core/java/com/android/server/am/AppTaskImpl.java index d42a3b9bc1f0..38b3039f1856 100644 --- a/services/core/java/com/android/server/am/AppTaskImpl.java +++ b/services/core/java/com/android/server/am/AppTaskImpl.java @@ -16,6 +16,7 @@ package com.android.server.am; +import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS; import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS; import android.app.ActivityManager; @@ -76,7 +77,8 @@ class AppTaskImpl extends IAppTask.Stub { synchronized (mService) { long origId = Binder.clearCallingIdentity(); try { - TaskRecord tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId); + TaskRecord tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId, + MATCH_TASK_IN_STACKS_OR_RECENT_TASKS); if (tr == null) { throw new IllegalArgumentException("Unable to find task ID " + mTaskId); } @@ -110,7 +112,8 @@ class AppTaskImpl extends IAppTask.Stub { TaskRecord tr; IApplicationThread appThread; synchronized (mService) { - tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId); + tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId, + MATCH_TASK_IN_STACKS_OR_RECENT_TASKS); if (tr == null) { throw new IllegalArgumentException("Unable to find task ID " + mTaskId); } @@ -131,7 +134,8 @@ class AppTaskImpl extends IAppTask.Stub { synchronized (mService) { long origId = Binder.clearCallingIdentity(); try { - TaskRecord tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId); + TaskRecord tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId, + MATCH_TASK_IN_STACKS_OR_RECENT_TASKS); if (tr == null) { throw new IllegalArgumentException("Unable to find task ID " + mTaskId); } diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 68ed9aecf31b..db12ae252abc 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -297,31 +297,39 @@ public final class BatteryStatsService extends IBatteryStats.Stub void noteProcessStart(String name, int uid) { synchronized (mStats) { mStats.noteProcessStartLocked(name, uid); + // TODO: decide where this should be and use a constant instead of a literal. + StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name, 1); } } void noteProcessCrash(String name, int uid) { synchronized (mStats) { mStats.noteProcessCrashLocked(name, uid); + // TODO: decide where this should be and use a constant instead of a literal. + StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name, 2); } } void noteProcessAnr(String name, int uid) { synchronized (mStats) { mStats.noteProcessAnrLocked(name, uid); + // TODO: decide where this should be and use a constant instead of a literal. + StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name, 3); } } void noteProcessFinish(String name, int uid) { synchronized (mStats) { mStats.noteProcessFinishLocked(name, uid); + // TODO: decide where this should be and use a constant instead of a literal. + StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name, 0); } } void noteUidProcessState(int uid, int state) { synchronized (mStats) { // TODO: remove this once we figure out properly where and how - StatsLog.write(StatsLog.PROCESS_STATE_CHANGED, uid, state); + StatsLog.write(StatsLog.UID_PROCESS_STATE_CHANGED, uid, state); mStats.noteUidProcessStateLocked(uid, state); } @@ -548,6 +556,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub public void noteScreenBrightness(int brightness) { enforceCallingPermission(); synchronized (mStats) { + StatsLog.write(StatsLog.SCREEN_BRIGHTNESS_CHANGED, brightness); mStats.noteScreenBrightnessLocked(brightness); } } @@ -909,6 +918,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub enforceCallingPermission(); synchronized (mStats) { mStats.noteDeviceIdleModeLocked(mode, activeReason, activeUid); + StatsLog.write(StatsLog.DEVICE_IDLE_MODE_STATE_CHANGED, mode); } } diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index c62cc38b716f..aa82d000386a 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -1198,11 +1198,6 @@ public final class BroadcastQueue { + " (uid " + r.callingUid + ")"); skip = true; } - if (!skip) { - r.manifestCount++; - } else { - r.manifestSkipCount++; - } if (r.curApp != null && r.curApp.crashing) { // If the target process is crashing, just skip it. Slog.w(TAG, "Skipping deliver ordered [" + mQueueName + "] " + r @@ -1283,6 +1278,16 @@ public final class BroadcastQueue { } } + if (!skip && !Intent.ACTION_SHUTDOWN.equals(r.intent.getAction()) + && !mService.mUserController + .isUserRunning(UserHandle.getUserId(info.activityInfo.applicationInfo.uid), + 0 /* flags */)) { + skip = true; + Slog.w(TAG, + "Skipping delivery to " + info.activityInfo.packageName + " / " + + info.activityInfo.applicationInfo.uid + " : user is not running"); + } + if (skip) { if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Skipping delivery of ordered [" + mQueueName + "] " @@ -1291,9 +1296,11 @@ public final class BroadcastQueue { r.receiver = null; r.curFilter = null; r.state = BroadcastRecord.IDLE; + r.manifestSkipCount++; scheduleBroadcastsLocked(); return; } + r.manifestCount++; r.delivery[recIdx] = BroadcastRecord.DELIVERY_DELIVERED; r.state = BroadcastRecord.APP_RECEIVE; @@ -1302,7 +1309,7 @@ public final class BroadcastQueue { if (DEBUG_MU && r.callingUid > UserHandle.PER_USER_RANGE) { Slog.v(TAG_MU, "Updated broadcast record activity info for secondary user, " + info.activityInfo + ", callingUid = " + r.callingUid + ", uid = " - + info.activityInfo.applicationInfo.uid); + + receiverUid); } if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) { @@ -1365,7 +1372,7 @@ public final class BroadcastQueue { // and mark the broadcast record as ready for the next. Slog.w(TAG, "Unable to launch app " + info.activityInfo.applicationInfo.packageName + "/" - + info.activityInfo.applicationInfo.uid + " for broadcast " + + receiverUid + " for broadcast " + r.intent + ": process is bad"); logBroadcastReceiverDiscardLocked(r); finishReceiverLocked(r, r.resultCode, r.resultData, diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index c4512353db94..4080c27b4ecc 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -231,9 +231,6 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi private boolean mSupportsPictureInPicture; // Whether or not this task and its activities // support PiP. Based on the {@link ActivityInfo#FLAG_SUPPORTS_PICTURE_IN_PICTURE} flag // of the root activity. - boolean mTemporarilyUnresizable; // Separate flag from mResizeMode used to suppress resize - // changes on a temporary basis. - /** Can't be put in lockTask mode. */ final static int LOCK_TASK_AUTH_DONT_LOCK = 0; /** Can enter app pinning with user approval. Can never start over existing lockTask task. */ @@ -1457,7 +1454,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi private boolean isResizeable(boolean checkSupportsPip) { return (mService.mForceResizableActivities || ActivityInfo.isResizeableMode(mResizeMode) - || (checkSupportsPip && mSupportsPictureInPicture)) && !mTemporarilyUnresizable; + || (checkSupportsPip && mSupportsPictureInPicture)); } boolean isResizeable() { diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java index 6206dfcd6622..05c6e69c1895 100644 --- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java +++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java @@ -27,6 +27,7 @@ import android.net.metrics.ConnectStats; import android.net.metrics.DnsEvent; import android.net.metrics.INetdEventListener; import android.net.metrics.IpConnectivityLog; +import android.net.metrics.NetworkMetrics; import android.net.metrics.WakeupEvent; import android.net.metrics.WakeupStats; import android.os.RemoteException; @@ -34,6 +35,7 @@ import android.text.format.DateUtils; import android.util.Log; import android.util.ArrayMap; import android.util.SparseArray; + import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.BitUtils; @@ -41,10 +43,11 @@ import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.RingBuffer; import com.android.internal.util.TokenBucket; import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent; + import java.io.PrintWriter; +import java.util.ArrayList; import java.util.List; -import java.util.function.Function; -import java.util.function.IntFunction; +import java.util.StringJoiner; /** * Implementation of the INetdEventListener interface. @@ -57,13 +60,13 @@ public class NetdEventListenerService extends INetdEventListener.Stub { private static final boolean DBG = false; private static final boolean VDBG = false; - private static final int INITIAL_DNS_BATCH_SIZE = 100; - // Rate limit connect latency logging to 1 measurement per 15 seconds (5760 / day) with maximum // bursts of 5000 measurements. private static final int CONNECT_LATENCY_BURST_LIMIT = 5000; private static final int CONNECT_LATENCY_FILL_RATE = 15 * (int) DateUtils.SECOND_IN_MILLIS; - private static final int CONNECT_LATENCY_MAXIMUM_RECORDS = 20000; + + private static final long METRICS_SNAPSHOT_SPAN_MS = 5 * DateUtils.MINUTE_IN_MILLIS; + private static final int METRICS_SNAPSHOT_BUFFER_SIZE = 48; // 4 hours @VisibleForTesting static final int WAKEUP_EVENT_BUFFER_LENGTH = 1024; @@ -72,11 +75,15 @@ public class NetdEventListenerService extends INetdEventListener.Stub { @VisibleForTesting static final String WAKEUP_EVENT_IFACE_PREFIX = "iface:"; - // Sparse arrays of DNS and connect events, grouped by net id. + // Array of aggregated DNS and connect events sent by netd, grouped by net id. + @GuardedBy("this") + private final SparseArray<NetworkMetrics> mNetworkMetrics = new SparseArray<>(); + @GuardedBy("this") - private final SparseArray<DnsEvent> mDnsEvents = new SparseArray<>(); + private final RingBuffer<NetworkMetricsSnapshot> mNetworkMetricsSnapshots = + new RingBuffer<>(NetworkMetricsSnapshot.class, METRICS_SNAPSHOT_BUFFER_SIZE); @GuardedBy("this") - private final SparseArray<ConnectStats> mConnectEvents = new SparseArray<>(); + private long mLastSnapshot = 0; // Array of aggregated wakeup event stats, grouped by interface name. @GuardedBy("this") @@ -84,7 +91,7 @@ public class NetdEventListenerService extends INetdEventListener.Stub { // Ring buffer array for storing packet wake up events sent by Netd. @GuardedBy("this") private final RingBuffer<WakeupEvent> mWakeupEvents = - new RingBuffer(WakeupEvent.class, WAKEUP_EVENT_BUFFER_LENGTH); + new RingBuffer<>(WakeupEvent.class, WAKEUP_EVENT_BUFFER_LENGTH); private final ConnectivityManager mCm; @@ -116,6 +123,41 @@ public class NetdEventListenerService extends INetdEventListener.Stub { mCm = cm; } + private static long projectSnapshotTime(long timeMs) { + return (timeMs / METRICS_SNAPSHOT_SPAN_MS) * METRICS_SNAPSHOT_SPAN_MS; + } + + private NetworkMetrics getMetricsForNetwork(long timeMs, int netId) { + collectPendingMetricsSnapshot(timeMs); + NetworkMetrics metrics = mNetworkMetrics.get(netId); + if (metrics == null) { + // TODO: allow to change transport for a given netid. + metrics = new NetworkMetrics(netId, getTransports(netId), mConnectTb); + mNetworkMetrics.put(netId, metrics); + } + return metrics; + } + + private NetworkMetricsSnapshot[] getNetworkMetricsSnapshots() { + collectPendingMetricsSnapshot(System.currentTimeMillis()); + return mNetworkMetricsSnapshots.toArray(); + } + + private void collectPendingMetricsSnapshot(long timeMs) { + // Detects time differences larger than the snapshot collection period. + // This is robust against clock jumps and long inactivity periods. + if (Math.abs(timeMs - mLastSnapshot) <= METRICS_SNAPSHOT_SPAN_MS) { + return; + } + mLastSnapshot = projectSnapshotTime(timeMs); + NetworkMetricsSnapshot snapshot = + NetworkMetricsSnapshot.collect(mLastSnapshot, mNetworkMetrics); + if (snapshot.stats.isEmpty()) { + return; + } + mNetworkMetricsSnapshots.append(snapshot); + } + @Override // Called concurrently by multiple binder threads. // This method must not block or perform long-running operations. @@ -124,15 +166,10 @@ public class NetdEventListenerService extends INetdEventListener.Stub { throws RemoteException { maybeVerboseLog("onDnsEvent(%d, %d, %d, %dms)", netId, eventType, returnCode, latencyMs); - DnsEvent dnsEvent = mDnsEvents.get(netId); - if (dnsEvent == null) { - dnsEvent = makeDnsEvent(netId); - mDnsEvents.put(netId, dnsEvent); - } - dnsEvent.addResult((byte) eventType, (byte) returnCode, latencyMs); + long timestamp = System.currentTimeMillis(); + getMetricsForNetwork(timestamp, netId).addDnsResult(eventType, returnCode, latencyMs); if (mNetdEventCallback != null) { - long timestamp = System.currentTimeMillis(); mNetdEventCallback.onDnsEvent(hostname, ipAddresses, ipAddressesCount, timestamp, uid); } } @@ -144,15 +181,11 @@ public class NetdEventListenerService extends INetdEventListener.Stub { int port, int uid) throws RemoteException { maybeVerboseLog("onConnectEvent(%d, %d, %dms)", netId, error, latencyMs); - ConnectStats connectStats = mConnectEvents.get(netId); - if (connectStats == null) { - connectStats = makeConnectStats(netId); - mConnectEvents.put(netId, connectStats); - } - connectStats.addEvent(error, latencyMs, ipAddr); + long timestamp = System.currentTimeMillis(); + getMetricsForNetwork(timestamp, netId).addConnectResult(error, latencyMs, ipAddr); if (mNetdEventCallback != null) { - mNetdEventCallback.onConnectEvent(ipAddr, port, System.currentTimeMillis(), uid); + mNetdEventCallback.onConnectEvent(ipAddr, port, timestamp, uid); } } @@ -189,11 +222,24 @@ public class NetdEventListenerService extends INetdEventListener.Stub { } public synchronized void flushStatistics(List<IpConnectivityEvent> events) { - flushProtos(events, mConnectEvents, IpConnectivityEventBuilder::toProto); - flushProtos(events, mDnsEvents, IpConnectivityEventBuilder::toProto); + for (int i = 0; i < mNetworkMetrics.size(); i++) { + ConnectStats stats = mNetworkMetrics.valueAt(i).connectMetrics; + if (stats.eventCount == 0) { + continue; + } + events.add(IpConnectivityEventBuilder.toProto(stats)); + } + for (int i = 0; i < mNetworkMetrics.size(); i++) { + DnsEvent ev = mNetworkMetrics.valueAt(i).dnsMetrics; + if (ev.eventCount == 0) { + continue; + } + events.add(IpConnectivityEventBuilder.toProto(ev)); + } for (int i = 0; i < mWakeupStats.size(); i++) { events.add(IpConnectivityEventBuilder.toProto(mWakeupStats.valueAt(i))); } + mNetworkMetrics.clear(); mWakeupStats.clear(); } @@ -206,8 +252,15 @@ public class NetdEventListenerService extends INetdEventListener.Stub { } public synchronized void list(PrintWriter pw) { - listEvents(pw, mConnectEvents, (x) -> x, "\n"); - listEvents(pw, mDnsEvents, (x) -> x, "\n"); + for (int i = 0; i < mNetworkMetrics.size(); i++) { + pw.println(mNetworkMetrics.valueAt(i).connectMetrics); + } + for (int i = 0; i < mNetworkMetrics.size(); i++) { + pw.println(mNetworkMetrics.valueAt(i).dnsMetrics); + } + for (NetworkMetricsSnapshot s : getNetworkMetricsSnapshots()) { + pw.println(s); + } for (int i = 0; i < mWakeupStats.size(); i++) { pw.println(mWakeupStats.valueAt(i)); } @@ -217,41 +270,17 @@ public class NetdEventListenerService extends INetdEventListener.Stub { } public synchronized void listAsProtos(PrintWriter pw) { - listEvents(pw, mConnectEvents, IpConnectivityEventBuilder::toProto, ""); - listEvents(pw, mDnsEvents, IpConnectivityEventBuilder::toProto, ""); - for (int i = 0; i < mWakeupStats.size(); i++) { - pw.print(IpConnectivityEventBuilder.toProto(mWakeupStats.valueAt(i))); + for (int i = 0; i < mNetworkMetrics.size(); i++) { + pw.print(IpConnectivityEventBuilder.toProto(mNetworkMetrics.valueAt(i).connectMetrics)); } - } - - private static <T> void flushProtos(List<IpConnectivityEvent> out, SparseArray<T> in, - Function<T, IpConnectivityEvent> mapper) { - for (int i = 0; i < in.size(); i++) { - out.add(mapper.apply(in.valueAt(i))); + for (int i = 0; i < mNetworkMetrics.size(); i++) { + pw.print(IpConnectivityEventBuilder.toProto(mNetworkMetrics.valueAt(i).dnsMetrics)); } - in.clear(); - } - - private static <T> void listEvents( - PrintWriter pw, SparseArray<T> events, Function<T, Object> mapper, String separator) { - // Proto derived Classes have toString method that adds a \n at the end. - // Let the caller control that by passing in the line separator explicitly. - for (int i = 0; i < events.size(); i++) { - pw.print(mapper.apply(events.valueAt(i))); - pw.print(separator); + for (int i = 0; i < mWakeupStats.size(); i++) { + pw.print(IpConnectivityEventBuilder.toProto(mWakeupStats.valueAt(i))); } } - private ConnectStats makeConnectStats(int netId) { - long transports = getTransports(netId); - return new ConnectStats(netId, transports, mConnectTb, CONNECT_LATENCY_MAXIMUM_RECORDS); - } - - private DnsEvent makeDnsEvent(int netId) { - long transports = getTransports(netId); - return new DnsEvent(netId, transports, INITIAL_DNS_BATCH_SIZE); - } - private long getTransports(int netId) { // TODO: directly query ConnectivityService instead of going through Binder interface. NetworkCapabilities nc = mCm.getNetworkCapabilities(new Network(netId)); @@ -268,4 +297,32 @@ public class NetdEventListenerService extends INetdEventListener.Stub { private static void maybeVerboseLog(String s, Object... args) { if (VDBG) Log.d(TAG, String.format(s, args)); } + + /** Helper class for buffering summaries of NetworkMetrics at regular time intervals */ + static class NetworkMetricsSnapshot { + + public long timeMs; + public List<NetworkMetrics.Summary> stats = new ArrayList<>(); + + static NetworkMetricsSnapshot collect(long timeMs, SparseArray<NetworkMetrics> networkMetrics) { + NetworkMetricsSnapshot snapshot = new NetworkMetricsSnapshot(); + snapshot.timeMs = timeMs; + for (int i = 0; i < networkMetrics.size(); i++) { + NetworkMetrics.Summary s = networkMetrics.valueAt(i).getPendingStats(); + if (s != null) { + snapshot.stats.add(s); + } + } + return snapshot; + } + + @Override + public String toString() { + StringJoiner j = new StringJoiner(", "); + for (NetworkMetrics.Summary s : stats) { + j.add(s.toString()); + } + return String.format("%tT.%tL: %s", timeMs, timeMs, j.toString()); + } + } } diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index e41c17df8ca1..4cf35bc4accb 100644 --- a/services/core/java/com/android/server/location/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/GnssLocationProvider.java @@ -669,9 +669,11 @@ public class GnssLocationProvider implements LocationProviderInterface { for (String item : configValues) { if (DEBUG) Log.d(TAG, "GpsParamsResource: " + item); // We need to support "KEY =", but not "=VALUE". - String[] split = item.split("="); - if (split.length == 2) { - properties.setProperty(split[0].trim().toUpperCase(), split[1]); + int index = item.indexOf("="); + if (index > 0 && index + 1 < item.length()) { + String key = item.substring(0, index); + String value = item.substring(index + 1); + properties.setProperty(key.trim().toUpperCase(), value); } else { Log.w(TAG, "malformed contents: " + item); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 391deb746e5c..7fb2ec4601c3 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -753,9 +753,6 @@ public class PackageManagerService extends IPackageManager.Stub PackageManagerInternal.ExternalSourcesPolicy mExternalSourcesPolicy; - // System configuration read by SystemConfig. - final int[] mGlobalGids; - final SparseArray<ArraySet<String>> mSystemPermissions; @GuardedBy("mAvailableFeatures") final ArrayMap<String, FeatureInfo> mAvailableFeatures; @@ -938,10 +935,6 @@ public class PackageManagerService extends IPackageManager.Stub final ArrayMap<ComponentName, PackageParser.Instrumentation> mInstrumentation = new ArrayMap<ComponentName, PackageParser.Instrumentation>(); - // Mapping from permission names to info about them. - final ArrayMap<String, PackageParser.PermissionGroup> mPermissionGroups = - new ArrayMap<String, PackageParser.PermissionGroup>(); - // Packages whose data we have transfered into another package, thus // should no longer exist. final ArraySet<String> mTransferedPackages = new ArraySet<String>(); @@ -2434,8 +2427,6 @@ public class PackageManagerService extends IPackageManager.Stub Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "get system config"); SystemConfig systemConfig = SystemConfig.getInstance(); - mGlobalGids = systemConfig.getGlobalGids(); - mSystemPermissions = systemConfig.getSystemPermissions(); mAvailableFeatures = systemConfig.getAvailableFeatures(); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); @@ -4228,44 +4219,22 @@ public class PackageManagerService extends IPackageManager.Stub @Override public @Nullable ParceledListSlice<PermissionInfo> queryPermissionsByGroup(String groupName, int flags) { - // TODO Move this to PermissionManager when mPermissionGroups is moved there - synchronized (mPackages) { - if (groupName != null && !mPermissionGroups.containsKey(groupName)) { - // This is thrown as NameNotFoundException - return null; - } - } - return new ParceledListSlice<>( - mPermissionManager.getPermissionInfoByGroup(groupName, flags, getCallingUid())); + final List<PermissionInfo> permissionList = + mPermissionManager.getPermissionInfoByGroup(groupName, flags, getCallingUid()); + return (permissionList == null) ? null : new ParceledListSlice<>(permissionList); } @Override - public PermissionGroupInfo getPermissionGroupInfo(String name, int flags) { - if (getInstantAppPackageName(Binder.getCallingUid()) != null) { - return null; - } - // reader - synchronized (mPackages) { - return PackageParser.generatePermissionGroupInfo( - mPermissionGroups.get(name), flags); - } + public PermissionGroupInfo getPermissionGroupInfo(String groupName, int flags) { + return mPermissionManager.getPermissionGroupInfo(groupName, flags, getCallingUid()); } @Override public @NonNull ParceledListSlice<PermissionGroupInfo> getAllPermissionGroups(int flags) { - if (getInstantAppPackageName(Binder.getCallingUid()) != null) { - return ParceledListSlice.emptyList(); - } - // reader - synchronized (mPackages) { - final int N = mPermissionGroups.size(); - ArrayList<PermissionGroupInfo> out - = new ArrayList<PermissionGroupInfo>(N); - for (PackageParser.PermissionGroup pg : mPermissionGroups.values()) { - out.add(PackageParser.generatePermissionGroupInfo(pg, flags)); - } - return new ParceledListSlice<>(out); - } + final List<PermissionGroupInfo> permissionList = + mPermissionManager.getAllPermissionGroups(flags, getCallingUid()); + return (permissionList == null) + ? ParceledListSlice.emptyList() : new ParceledListSlice<>(permissionList); } private ApplicationInfo generateApplicationInfoFromSettingsLPw(String packageName, int flags, @@ -5138,59 +5107,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public int checkUidPermission(String permName, int uid) { - final int callingUid = Binder.getCallingUid(); - final int callingUserId = UserHandle.getUserId(callingUid); - final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null; - final boolean isUidInstantApp = getInstantAppPackageName(uid) != null; - final int userId = UserHandle.getUserId(uid); - if (!sUserManager.exists(userId)) { - return PackageManager.PERMISSION_DENIED; - } - - synchronized (mPackages) { - Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid)); - if (obj != null) { - if (obj instanceof SharedUserSetting) { - if (isCallerInstantApp) { - return PackageManager.PERMISSION_DENIED; - } - } else if (obj instanceof PackageSetting) { - final PackageSetting ps = (PackageSetting) obj; - if (filterAppAccessLPr(ps, callingUid, callingUserId)) { - return PackageManager.PERMISSION_DENIED; - } - } - final SettingBase settingBase = (SettingBase) obj; - final PermissionsState permissionsState = settingBase.getPermissionsState(); - if (permissionsState.hasPermission(permName, userId)) { - if (isUidInstantApp) { - if (mSettings.mPermissions.isPermissionInstant(permName)) { - return PackageManager.PERMISSION_GRANTED; - } - } else { - return PackageManager.PERMISSION_GRANTED; - } - } - // Special case: ACCESS_FINE_LOCATION permission includes ACCESS_COARSE_LOCATION - if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && permissionsState - .hasPermission(Manifest.permission.ACCESS_FINE_LOCATION, userId)) { - return PackageManager.PERMISSION_GRANTED; - } - } else { - ArraySet<String> perms = mSystemPermissions.get(uid); - if (perms != null) { - if (perms.contains(permName)) { - return PackageManager.PERMISSION_GRANTED; - } - if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && perms - .contains(Manifest.permission.ACCESS_FINE_LOCATION)) { - return PackageManager.PERMISSION_GRANTED; - } - } - } - } - - return PackageManager.PERMISSION_DENIED; + return mPermissionManager.checkUidPermission(permName, uid, getCallingUid()); } @Override @@ -11156,54 +11073,15 @@ public class PackageManagerService extends IPackageManager.Stub if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Activities: " + r); } - N = pkg.permissionGroups.size(); - r = null; - for (i=0; i<N; i++) { - PackageParser.PermissionGroup pg = pkg.permissionGroups.get(i); - PackageParser.PermissionGroup cur = mPermissionGroups.get(pg.info.name); - final String curPackageName = cur == null ? null : cur.info.packageName; - // Dont allow ephemeral apps to define new permission groups. - if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) { - Slog.w(TAG, "Permission group " + pg.info.name + " from package " - + pg.info.packageName - + " ignored: instant apps cannot define new permission groups."); - continue; - } - final boolean isPackageUpdate = pg.info.packageName.equals(curPackageName); - if (cur == null || isPackageUpdate) { - mPermissionGroups.put(pg.info.name, pg); - if (chatty) { - if (r == null) { - r = new StringBuilder(256); - } else { - r.append(' '); - } - if (isPackageUpdate) { - r.append("UPD:"); - } - r.append(pg.info.name); - } - } else { - Slog.w(TAG, "Permission group " + pg.info.name + " from package " - + pg.info.packageName + " ignored: original from " - + cur.info.packageName); - if (chatty) { - if (r == null) { - r = new StringBuilder(256); - } else { - r.append(' '); - } - r.append("DUP:"); - r.append(pg.info.name); - } - } - } - if (r != null) { - if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Permission Groups: " + r); + // Don't allow ephemeral applications to define new permissions groups. + if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) { + Slog.w(TAG, "Permission groups from package " + pkg.packageName + + " ignored: instant apps cannot define new permission groups."); + } else { + mPermissionManager.addAllPermissionGroups(pkg, chatty); } - - // Dont allow ephemeral apps to define new permissions. + // Don't allow ephemeral applications to define new permissions. if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) { Slog.w(TAG, "Permissions from package " + pkg.packageName + " ignored: instant apps cannot define new permissions."); @@ -12107,7 +11985,7 @@ public class PackageManagerService extends IPackageManager.Stub } } - permissionsState.setGlobalGids(mGlobalGids); + permissionsState.setGlobalGids(mPermissionManager.getGlobalGidsTEMP()); final int N = pkg.requestedPermissions.size(); for (int i=0; i<N; i++) { @@ -23606,13 +23484,6 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } @Override - public PackageParser.PermissionGroup getPermissionGroupTEMP(String groupName) { - synchronized (mPackages) { - return mPermissionGroups.get(groupName); - } - } - - @Override public boolean isInstantApp(String packageName, int userId) { return PackageManagerService.this.isInstantApp(packageName, userId); } diff --git a/services/core/java/com/android/server/pm/UserDataPreparer.java b/services/core/java/com/android/server/pm/UserDataPreparer.java index b8b00af448eb..bfe09b8ecbce 100644 --- a/services/core/java/com/android/server/pm/UserDataPreparer.java +++ b/services/core/java/com/android/server/pm/UserDataPreparer.java @@ -132,11 +132,9 @@ class UserDataPreparer { if ((flags & StorageManager.FLAG_STORAGE_DE) != 0) { FileUtils.deleteContentsAndDir(getUserSystemDirectory(userId)); FileUtils.deleteContentsAndDir(getDataSystemDeDirectory(userId)); - FileUtils.deleteContentsAndDir(getDataMiscDeDirectory(userId)); } if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) { FileUtils.deleteContentsAndDir(getDataSystemCeDirectory(userId)); - FileUtils.deleteContentsAndDir(getDataMiscCeDirectory(userId)); } } diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java index 8aac52ae0df7..9605fcc65664 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java @@ -19,6 +19,7 @@ package com.android.server.pm.permission; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.pm.PackageParser; +import android.content.pm.PermissionGroupInfo; import android.content.pm.PermissionInfo; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManager.PermissionInfoFlags; @@ -89,6 +90,7 @@ public abstract class PermissionManagerInternal { * the permission settings. */ public abstract void addAllPermissions(@NonNull PackageParser.Package pkg, boolean chatty); + public abstract void addAllPermissionGroups(@NonNull PackageParser.Package pkg, boolean chatty); public abstract void removeAllPermissions(@NonNull PackageParser.Package pkg, boolean chatty); public abstract boolean addDynamicPermission(@NonNull PermissionInfo info, boolean async, int callingUid, @Nullable PermissionCallback callback); @@ -105,6 +107,16 @@ public abstract class PermissionManagerInternal { public abstract int getPermissionFlags(@NonNull String permName, @NonNull String packageName, int callingUid, int userId); /** + * Retrieve all of the information we know about a particular group of permissions. + */ + public abstract @Nullable PermissionGroupInfo getPermissionGroupInfo( + @NonNull String groupName, int flags, int callingUid); + /** + * Retrieve all of the known permission groups in the system. + */ + public abstract @Nullable List<PermissionGroupInfo> getAllPermissionGroups(int flags, + int callingUid); + /** * Retrieve all of the information we know about a particular permission. */ public abstract @Nullable PermissionInfo getPermissionInfo(@NonNull String permName, @@ -132,6 +144,7 @@ public abstract class PermissionManagerInternal { public abstract int checkPermission(@NonNull String permName, @NonNull String packageName, int callingUid, int userId); + public abstract int checkUidPermission(String permName, int uid, int callingUid); /** * Enforces the request is from the system or an app that has INTERACT_ACROSS_USERS @@ -147,8 +160,6 @@ public abstract class PermissionManagerInternal { public abstract @NonNull DefaultPermissionGrantPolicy getDefaultPermissionGrantPolicy(); /** HACK HACK methods to allow for partial migration of data to the PermissionManager class */ - public abstract Iterator<BasePermission> getPermissionIteratorTEMP(); public abstract @Nullable BasePermission getPermissionTEMP(@NonNull String permName); - public abstract void putPermissionTEMP(@NonNull String permName, - @NonNull BasePermission permission); + public abstract @Nullable int[] getGlobalGidsTEMP(); }
\ No newline at end of file diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index d2d857caa240..a94a00ac1113 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -18,6 +18,7 @@ package com.android.server.pm.permission; import static android.Manifest.permission.READ_EXTERNAL_STORAGE; import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; +import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; import android.Manifest; import android.annotation.NonNull; @@ -27,7 +28,7 @@ import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.PackageParser; -import android.content.pm.ParceledListSlice; +import android.content.pm.PermissionGroupInfo; import android.content.pm.PermissionInfo; import android.content.pm.PackageParser.Package; import android.os.Binder; @@ -43,6 +44,7 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import android.util.Slog; +import android.util.SparseArray; import com.android.internal.R; import com.android.internal.logging.MetricsLogger; @@ -58,6 +60,7 @@ import com.android.server.pm.PackageManagerServiceUtils; import com.android.server.pm.PackageSetting; import com.android.server.pm.ProcessLoggingHandler; import com.android.server.pm.SharedUserSetting; +import com.android.server.pm.UserManagerService; import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback; import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback; import com.android.server.pm.permission.PermissionsState.PermissionState; @@ -122,6 +125,10 @@ public class PermissionManagerService { /** Default permission policy to provide proper behaviour out-of-the-box */ private final DefaultPermissionGrantPolicy mDefaultPermissionGrantPolicy; + // System configuration read by SystemConfig. + private final SparseArray<ArraySet<String>> mSystemPermissions; + private final int[] mGlobalGids; + /** Internal storage for permissions and related settings */ private final PermissionSettings mSettings; @@ -146,6 +153,9 @@ public class PermissionManagerService { mDefaultPermissionGrantPolicy = new DefaultPermissionGrantPolicy( context, mHandlerThread.getLooper(), defaultGrantCallback, this); + SystemConfig systemConfig = SystemConfig.getInstance(); + mSystemPermissions = systemConfig.getSystemPermissions(); + mGlobalGids = systemConfig.getGlobalGids(); // propagate permission configuration final ArrayMap<String, SystemConfig.PermissionEntry> permConfig = @@ -230,14 +240,94 @@ public class PermissionManagerService { return PackageManager.PERMISSION_DENIED; } - private PermissionInfo getPermissionInfo(String name, String packageName, int flags, + private int checkUidPermission(String permName, int uid, int callingUid) { + final int callingUserId = UserHandle.getUserId(callingUid); + final boolean isCallerInstantApp = + mPackageManagerInt.getInstantAppPackageName(callingUid) != null; + final boolean isUidInstantApp = + mPackageManagerInt.getInstantAppPackageName(uid) != null; + final int userId = UserHandle.getUserId(uid); + if (!mUserManagerInt.exists(userId)) { + return PackageManager.PERMISSION_DENIED; + } + + final String[] packages = mContext.getPackageManager().getPackagesForUid(uid); + if (packages != null && packages.length > 0) { + final PackageParser.Package pkg = mPackageManagerInt.getPackage(packages[0]); + if (pkg.mSharedUserId != null) { + if (isCallerInstantApp) { + return PackageManager.PERMISSION_DENIED; + } + } else { + if (mPackageManagerInt.filterAppAccess(pkg, callingUid, callingUserId)) { + return PackageManager.PERMISSION_DENIED; + } + } + final PermissionsState permissionsState = + ((PackageSetting) pkg.mExtras).getPermissionsState(); + if (permissionsState.hasPermission(permName, userId)) { + if (isUidInstantApp) { + if (mSettings.isPermissionInstant(permName)) { + return PackageManager.PERMISSION_GRANTED; + } + } else { + return PackageManager.PERMISSION_GRANTED; + } + } + // Special case: ACCESS_FINE_LOCATION permission includes ACCESS_COARSE_LOCATION + if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && permissionsState + .hasPermission(Manifest.permission.ACCESS_FINE_LOCATION, userId)) { + return PackageManager.PERMISSION_GRANTED; + } + } else { + ArraySet<String> perms = mSystemPermissions.get(uid); + if (perms != null) { + if (perms.contains(permName)) { + return PackageManager.PERMISSION_GRANTED; + } + if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && perms + .contains(Manifest.permission.ACCESS_FINE_LOCATION)) { + return PackageManager.PERMISSION_GRANTED; + } + } + } + return PackageManager.PERMISSION_DENIED; + } + + private PermissionGroupInfo getPermissionGroupInfo(String groupName, int flags, + int callingUid) { + if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) { + return null; + } + synchronized (mLock) { + return PackageParser.generatePermissionGroupInfo( + mSettings.mPermissionGroups.get(groupName), flags); + } + } + + private List<PermissionGroupInfo> getAllPermissionGroups(int flags, int callingUid) { + if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) { + return null; + } + synchronized (mLock) { + final int N = mSettings.mPermissionGroups.size(); + final ArrayList<PermissionGroupInfo> out + = new ArrayList<PermissionGroupInfo>(N); + for (PackageParser.PermissionGroup pg : mSettings.mPermissionGroups.values()) { + out.add(PackageParser.generatePermissionGroupInfo(pg, flags)); + } + return out; + } + } + + private PermissionInfo getPermissionInfo(String permName, String packageName, int flags, int callingUid) { if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) { return null; } // reader synchronized (mLock) { - final BasePermission bp = mSettings.getPermissionLocked(name); + final BasePermission bp = mSettings.getPermissionLocked(permName); if (bp == null) { return null; } @@ -252,14 +342,10 @@ public class PermissionManagerService { if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) { return null; } - // reader synchronized (mLock) { - // TODO Uncomment when mPermissionGroups moves to this class -// if (groupName != null && !mPermissionGroups.containsKey(groupName)) { -// // This is thrown as NameNotFoundException -// return null; -// } - + if (groupName != null && !mSettings.mPermissionGroups.containsKey(groupName)) { + return null; + } final ArrayList<PermissionInfo> out = new ArrayList<PermissionInfo>(10); for (BasePermission bp : mSettings.mPermissions.values()) { final PermissionInfo pi = bp.generatePermissionInfo(groupName, flags); @@ -314,21 +400,21 @@ public class PermissionManagerService { // Assume by default that we did not install this permission into the system. p.info.flags &= ~PermissionInfo.FLAG_INSTALLED; - // Now that permission groups have a special meaning, we ignore permission - // groups for legacy apps to prevent unexpected behavior. In particular, - // permissions for one app being granted to someone just because they happen - // to be in a group defined by another app (before this had no implications). - if (pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) { - p.group = mPackageManagerInt.getPermissionGroupTEMP(p.info.group); - // Warn for a permission in an unknown group. - if (PackageManagerService.DEBUG_PERMISSIONS - && p.info.group != null && p.group == null) { - Slog.i(TAG, "Permission " + p.info.name + " from package " - + p.info.packageName + " in an unknown group " + p.info.group); + synchronized (PermissionManagerService.this.mLock) { + // Now that permission groups have a special meaning, we ignore permission + // groups for legacy apps to prevent unexpected behavior. In particular, + // permissions for one app being granted to someone just because they happen + // to be in a group defined by another app (before this had no implications). + if (pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) { + p.group = mSettings.mPermissionGroups.get(p.info.group); + // Warn for a permission in an unknown group. + if (PackageManagerService.DEBUG_PERMISSIONS + && p.info.group != null && p.group == null) { + Slog.i(TAG, "Permission " + p.info.name + " from package " + + p.info.packageName + " in an unknown group " + p.info.group); + } } - } - synchronized (PermissionManagerService.this.mLock) { if (p.tree) { final BasePermission bp = BasePermission.createOrUpdate( mSettings.getPermissionTreeLocked(p.info.name), p, pkg, @@ -344,6 +430,48 @@ public class PermissionManagerService { } } + private void addAllPermissionGroups(PackageParser.Package pkg, boolean chatty) { + final int N = pkg.permissionGroups.size(); + StringBuilder r = null; + for (int i=0; i<N; i++) { + final PackageParser.PermissionGroup pg = pkg.permissionGroups.get(i); + final PackageParser.PermissionGroup cur = mSettings.mPermissionGroups.get(pg.info.name); + final String curPackageName = (cur == null) ? null : cur.info.packageName; + final boolean isPackageUpdate = pg.info.packageName.equals(curPackageName); + if (cur == null || isPackageUpdate) { + mSettings.mPermissionGroups.put(pg.info.name, pg); + if (chatty && PackageManagerService.DEBUG_PACKAGE_SCANNING) { + if (r == null) { + r = new StringBuilder(256); + } else { + r.append(' '); + } + if (isPackageUpdate) { + r.append("UPD:"); + } + r.append(pg.info.name); + } + } else { + Slog.w(TAG, "Permission group " + pg.info.name + " from package " + + pg.info.packageName + " ignored: original from " + + cur.info.packageName); + if (chatty && PackageManagerService.DEBUG_PACKAGE_SCANNING) { + if (r == null) { + r = new StringBuilder(256); + } else { + r.append(' '); + } + r.append("DUP:"); + r.append(pg.info.name); + } + } + } + if (r != null && PackageManagerService.DEBUG_PACKAGE_SCANNING) { + Log.d(TAG, " Permission Groups: " + r); + } + + } + private void removeAllPermissions(PackageParser.Package pkg, boolean chatty) { synchronized (mLock) { int N = pkg.permissions.size(); @@ -1158,6 +1286,10 @@ public class PermissionManagerService { PermissionManagerService.this.addAllPermissions(pkg, chatty); } @Override + public void addAllPermissionGroups(Package pkg, boolean chatty) { + PermissionManagerService.this.addAllPermissionGroups(pkg, chatty); + } + @Override public void removeAllPermissions(Package pkg, boolean chatty) { PermissionManagerService.this.removeAllPermissions(pkg, chatty); } @@ -1252,6 +1384,20 @@ public class PermissionManagerService { permName, packageName, callingUid, userId); } @Override + public int checkUidPermission(String permName, int uid, int callingUid) { + return PermissionManagerService.this.checkUidPermission(permName, uid, callingUid); + } + @Override + public PermissionGroupInfo getPermissionGroupInfo(String groupName, int flags, + int callingUid) { + return PermissionManagerService.this.getPermissionGroupInfo( + groupName, flags, callingUid); + } + @Override + public List<PermissionGroupInfo> getAllPermissionGroups(int flags, int callingUid) { + return PermissionManagerService.this.getAllPermissionGroups(flags, callingUid); + } + @Override public PermissionInfo getPermissionInfo(String permName, String packageName, int flags, int callingUid) { return PermissionManagerService.this.getPermissionInfo( @@ -1277,15 +1423,9 @@ public class PermissionManagerService { } } @Override - public void putPermissionTEMP(String permName, BasePermission permission) { - synchronized (PermissionManagerService.this.mLock) { - mSettings.putPermissionLocked(permName, (BasePermission) permission); - } - } - @Override - public Iterator<BasePermission> getPermissionIteratorTEMP() { + public int[] getGlobalGidsTEMP() { synchronized (PermissionManagerService.this.mLock) { - return mSettings.getAllPermissionsLocked().iterator(); + return mGlobalGids; } } } diff --git a/services/core/java/com/android/server/pm/permission/PermissionSettings.java b/services/core/java/com/android/server/pm/permission/PermissionSettings.java index 7d125c9ebe87..0ed94a187af9 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionSettings.java +++ b/services/core/java/com/android/server/pm/permission/PermissionSettings.java @@ -19,6 +19,7 @@ package com.android.server.pm.permission; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.content.pm.PackageParser; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; @@ -64,6 +65,14 @@ public class PermissionSettings { new ArrayMap<String, BasePermission>(); /** + * All permisson groups know to the system. The mapping is from permission group + * name to permission group object. + */ + @GuardedBy("mLock") + final ArrayMap<String, PackageParser.PermissionGroup> mPermissionGroups = + new ArrayMap<String, PackageParser.PermissionGroup>(); + + /** * Set of packages that request a particular app op. The mapping is from permission * name to package names. */ diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java index 3fdafc7bebdf..21dffff011b4 100644 --- a/services/core/java/com/android/server/wm/DragState.java +++ b/services/core/java/com/android/server/wm/DragState.java @@ -22,6 +22,10 @@ import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACT import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; +import android.animation.Animator; +import android.animation.PropertyValuesHolder; +import android.animation.ValueAnimator; +import android.annotation.Nullable; import android.content.ClipData; import android.content.ClipDescription; import android.content.Context; @@ -29,7 +33,9 @@ import android.graphics.Matrix; import android.graphics.Point; import android.hardware.input.InputManager; import android.os.Build; +import android.os.Handler; import android.os.IBinder; +import android.os.Looper; import android.os.Message; import android.os.Process; import android.os.RemoteException; @@ -44,16 +50,12 @@ import android.view.InputChannel; import android.view.InputDevice; import android.view.PointerIcon; import android.view.SurfaceControl; +import android.view.SurfaceControl.Transaction; import android.view.View; import android.view.WindowManager; -import android.view.animation.AlphaAnimation; -import android.view.animation.Animation; -import android.view.animation.AnimationSet; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; -import android.view.animation.ScaleAnimation; import android.view.animation.Transformation; -import android.view.animation.TranslateAnimation; import com.android.server.input.InputApplicationHandle; import com.android.server.input.InputWindowHandle; @@ -78,8 +80,20 @@ class DragState { View.DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION | View.DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION; + // Property names for animations + private static final String ANIMATED_PROPERTY_X = "x"; + private static final String ANIMATED_PROPERTY_Y = "y"; + private static final String ANIMATED_PROPERTY_ALPHA = "alpha"; + private static final String ANIMATED_PROPERTY_SCALE = "scale"; + + // Messages for Handler. + private static final int MSG_ANIMATION_END = 0; + final WindowManagerService mService; IBinder mToken; + /** + * Do not use the variable from the out of animation thread while mAnimator is not null. + */ SurfaceControl mSurfaceControl; int mFlags; IBinder mLocalWin; @@ -101,10 +115,10 @@ class DragState { boolean mDragInProgress; DisplayContent mDisplayContent; - private Animation mAnimation; - final Transformation mTransformation = new Transformation(); + @Nullable private ValueAnimator mAnimator; private final Interpolator mCubicEaseOutInterpolator = new DecelerateInterpolator(1.5f); private Point mDisplaySize = new Point(); + private final Handler mHandler; DragState(WindowManagerService service, IBinder token, SurfaceControl surface, int flags, IBinder localWin) { @@ -114,9 +128,14 @@ class DragState { mFlags = flags; mLocalWin = localWin; mNotifiedWindows = new ArrayList<WindowState>(); + mHandler = new DragStateHandler(service.mH.getLooper()); } void reset() { + if (mAnimator != null) { + Slog.wtf(TAG_WM, + "Unexpectedly destroying mSurfaceControl while animation is running"); + } if (mSurfaceControl != null) { mSurfaceControl.destroy(); } @@ -388,11 +407,11 @@ class DragState { } void endDragLw() { - if (mAnimation != null) { + if (mAnimator != null) { return; } if (!mDragResult) { - mAnimation = createReturnAnimationLocked(); + mAnimator = createReturnAnimationLocked(); mService.scheduleAnimationLocked(); return; // Will call cleanUpDragLw when the animation is done. } @@ -400,11 +419,22 @@ class DragState { } void cancelDragLw() { - if (mAnimation != null) { + if (mAnimator != null) { + return; + } + if (!mDragInProgress) { + // This can happen if an app invokes Session#cancelDragAndDrop before + // Session#performDrag. Reset the drag state: + // 1. without sending the end broadcast because the start broadcast has not been sent, + // and + // 2. without playing the cancel animation because H.DRAG_START_TIMEOUT may be sent to + // WindowManagerService, which will cause DragState#reset() while playing the + // cancel animation. + reset(); + mService.mDragState = null; return; } - mAnimation = createCancelAnimationLocked(); - mService.scheduleAnimationLocked(); + mAnimator = createCancelAnimationLocked(); } private void cleanUpDragLw() { @@ -422,7 +452,7 @@ class DragState { } void notifyMoveLw(float x, float y) { - if (mAnimation != null) { + if (mAnimator != null) { return; } mCurrentX = x; @@ -491,7 +521,7 @@ class DragState { // dispatch the global drag-ended message, 'false' if we need to wait for a // result from the recipient. boolean notifyDropLw(float x, float y) { - if (mAnimation != null) { + if (mAnimator != null) { return false; } mCurrentX = x; @@ -560,56 +590,52 @@ class DragState { dragAndDropPermissions, result); } - boolean stepAnimationLocked(long currentTimeMs) { - if (mAnimation == null) { - return false; - } - - mTransformation.clear(); - if (!mAnimation.getTransformation(currentTimeMs, mTransformation)) { - cleanUpDragLw(); - return false; - } - - mTransformation.getMatrix().postTranslate( - mCurrentX - mThumbOffsetX, mCurrentY - mThumbOffsetY); - final float tmpFloats[] = mService.mTmpFloats; - mTransformation.getMatrix().getValues(tmpFloats); - mSurfaceControl.setPosition(tmpFloats[Matrix.MTRANS_X], tmpFloats[Matrix.MTRANS_Y]); - mSurfaceControl.setAlpha(mTransformation.getAlpha()); - mSurfaceControl.setMatrix(tmpFloats[Matrix.MSCALE_X], tmpFloats[Matrix.MSKEW_Y], - tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]); - return true; - } + private ValueAnimator createReturnAnimationLocked() { + final ValueAnimator animator = ValueAnimator.ofPropertyValuesHolder( + PropertyValuesHolder.ofFloat( + ANIMATED_PROPERTY_X, mCurrentX - mThumbOffsetX, + mOriginalX - mThumbOffsetX), + PropertyValuesHolder.ofFloat( + ANIMATED_PROPERTY_Y, mCurrentY - mThumbOffsetY, + mOriginalY - mThumbOffsetY), + PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_SCALE, 1, 1), + PropertyValuesHolder.ofFloat( + ANIMATED_PROPERTY_ALPHA, mOriginalAlpha, mOriginalAlpha / 2)); - private Animation createReturnAnimationLocked() { - final AnimationSet set = new AnimationSet(false); final float translateX = mOriginalX - mCurrentX; final float translateY = mOriginalY - mCurrentY; - set.addAnimation(new TranslateAnimation( 0, translateX, 0, translateY)); - set.addAnimation(new AlphaAnimation(mOriginalAlpha, mOriginalAlpha / 2)); // Adjust the duration to the travel distance. final double travelDistance = Math.sqrt(translateX * translateX + translateY * translateY); final double displayDiagonal = Math.sqrt(mDisplaySize.x * mDisplaySize.x + mDisplaySize.y * mDisplaySize.y); final long duration = MIN_ANIMATION_DURATION_MS + (long) (travelDistance / displayDiagonal * (MAX_ANIMATION_DURATION_MS - MIN_ANIMATION_DURATION_MS)); - set.setDuration(duration); - set.setInterpolator(mCubicEaseOutInterpolator); - set.initialize(0, 0, 0, 0); - set.start(); // Will start on the first call to getTransformation. - return set; + final AnimationListener listener = new AnimationListener(); + animator.setDuration(duration); + animator.setInterpolator(mCubicEaseOutInterpolator); + animator.addListener(listener); + animator.addUpdateListener(listener); + + mService.mAnimationHandler.post(() -> animator.start()); + return animator; } - private Animation createCancelAnimationLocked() { - final AnimationSet set = new AnimationSet(false); - set.addAnimation(new ScaleAnimation(1, 0, 1, 0, mThumbOffsetX, mThumbOffsetY)); - set.addAnimation(new AlphaAnimation(mOriginalAlpha, 0)); - set.setDuration(MIN_ANIMATION_DURATION_MS); - set.setInterpolator(mCubicEaseOutInterpolator); - set.initialize(0, 0, 0, 0); - set.start(); // Will start on the first call to getTransformation. - return set; + private ValueAnimator createCancelAnimationLocked() { + final ValueAnimator animator = ValueAnimator.ofPropertyValuesHolder( + PropertyValuesHolder.ofFloat( + ANIMATED_PROPERTY_X, mCurrentX - mThumbOffsetX, mCurrentX), + PropertyValuesHolder.ofFloat( + ANIMATED_PROPERTY_Y, mCurrentY - mThumbOffsetY, mCurrentY), + PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_SCALE, 1, 0), + PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_ALPHA, mOriginalAlpha, 0)); + final AnimationListener listener = new AnimationListener(); + animator.setDuration(MIN_ANIMATION_DURATION_MS); + animator.setInterpolator(mCubicEaseOutInterpolator); + animator.addListener(listener); + animator.addUpdateListener(listener); + + mService.mAnimationHandler.post(() -> animator.start()); + return animator; } private boolean isFromSource(int source) { @@ -622,4 +648,68 @@ class DragState { InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_GRABBING); } } + + private class DragStateHandler extends Handler { + DragStateHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_ANIMATION_END: + synchronized (mService.mWindowMap) { + if (mService.mDragState != DragState.this) { + Slog.wtf(TAG_WM, "mDragState is updated unexpectedly while " + + "playing animation"); + return; + } + if (mAnimator == null) { + Slog.wtf(TAG_WM, "Unexpected null mAnimator"); + return; + } + mAnimator = null; + cleanUpDragLw(); + } + break; + } + } + } + + private class AnimationListener + implements ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + try (final SurfaceControl.Transaction transaction = new SurfaceControl.Transaction()) { + transaction.setPosition( + mSurfaceControl, + (float) mAnimator.getAnimatedValue(ANIMATED_PROPERTY_X), + (float) mAnimator.getAnimatedValue(ANIMATED_PROPERTY_Y)); + transaction.setAlpha( + mSurfaceControl, + (float) mAnimator.getAnimatedValue(ANIMATED_PROPERTY_ALPHA)); + transaction.setMatrix( + mSurfaceControl, + (float) mAnimator.getAnimatedValue(ANIMATED_PROPERTY_SCALE), 0, + 0, (float) mAnimator.getAnimatedValue(ANIMATED_PROPERTY_SCALE)); + transaction.apply(); + } + } + + @Override + public void onAnimationStart(Animator animator) {} + + @Override + public void onAnimationCancel(Animator animator) {} + + @Override + public void onAnimationRepeat(Animator animator) {} + + @Override + public void onAnimationEnd(Animator animator) { + // Updating mDragState requires the WM lock so continues it on the out of + // AnimationThread. + mHandler.sendEmptyMessage(MSG_ANIMATION_END); + } + } } diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 4dd147e53588..2bb023557647 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -68,9 +68,7 @@ import java.util.Set; * This class represents an active client session. There is generally one * Session object per process that is interacting with the window manager. */ -// Needs to be public and not final so we can mock during tests...sucks I know :( -public class Session extends IWindowSession.Stub - implements IBinder.DeathRecipient { +class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { final WindowManagerService mService; final IWindowSessionCallback mCallback; final IInputMethodClient mClient; diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java index c01ee31e8c1c..e409a68f2dfe 100644 --- a/services/core/java/com/android/server/wm/WindowAnimator.java +++ b/services/core/java/com/android/server/wm/WindowAnimator.java @@ -223,10 +223,6 @@ public class WindowAnimator { } } - if (mService.mDragState != null) { - mAnimating |= mService.mDragState.stepAnimationLocked(mCurrentTime); - } - if (!mAnimating) { cancelAnimation(); } diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java index 679be1d40b60..9e4a9e9fdae9 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java @@ -57,7 +57,7 @@ public class ActivityRecordTests extends ActivityTestsBase { ComponentName.unflattenFromString("com.foo/.BarActivity2"); private ActivityManagerService mService; - private ActivityStack mStack; + private TestActivityStack mStack; private TaskRecord mTask; private ActivityRecord mActivity; @@ -76,13 +76,13 @@ public class ActivityRecordTests extends ActivityTestsBase { @Test public void testStackCleanupOnClearingTask() throws Exception { mActivity.setTask(null); - assertEquals(getActivityRemovedFromStackCount(), 1); + assertEquals(mStack.onActivityRemovedFromStackInvocationCount(), 1); } @Test public void testStackCleanupOnActivityRemoval() throws Exception { mTask.removeActivity(mActivity); - assertEquals(getActivityRemovedFromStackCount(), 1); + assertEquals(mStack.onActivityRemovedFromStackInvocationCount(), 1); } @Test @@ -97,7 +97,7 @@ public class ActivityRecordTests extends ActivityTestsBase { final TaskRecord newTask = createTask(mService.mStackSupervisor, testActivityComponent, mStack); mActivity.reparent(newTask, 0, null /*reason*/); - assertEquals(getActivityRemovedFromStackCount(), 0); + assertEquals(mStack.onActivityRemovedFromStackInvocationCount(), 0); } @Test @@ -129,15 +129,6 @@ public class ActivityRecordTests extends ActivityTestsBase { assertEquals(expectedActivityBounds, mActivity.getBounds()); } - private int getActivityRemovedFromStackCount() { - if (mStack instanceof ActivityStackReporter) { - return ((ActivityStackReporter) mStack).onActivityRemovedFromStackInvocationCount(); - } - - return -1; - } - - @Test public void testCanBeLaunchedOnDisplay() throws Exception { testSupportsLaunchingResizeable(false /*taskPresent*/, true /*taskResizeable*/, diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java index 4ee1f47d02e0..e17e51bb9da3 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java @@ -16,13 +16,19 @@ package com.android.server.am; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import android.content.ComponentName; import android.content.pm.ActivityInfo; @@ -45,7 +51,6 @@ import org.junit.Test; @Presubmit @RunWith(AndroidJUnit4.class) public class ActivityStackTests extends ActivityTestsBase { - private static final int TEST_STACK_ID = 100; private static final ComponentName testActivityComponent = ComponentName.unflattenFromString("com.foo/.BarActivity"); private static final ComponentName testOverlayComponent = @@ -127,4 +132,122 @@ public class ActivityStackTests extends ActivityTestsBase { assertEquals(mTask.getTopActivity(true /* includeOverlays */), taskOverlay); assertNotNull(result.r); } + + @Test + public void testShouldBeVisible_Fullscreen() throws Exception { + final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay(); + final TestActivityStack homeStack = createStackForShouldBeVisibleTest(display, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); + final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(display, + WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */); + + assertTrue(homeStack.shouldBeVisible(null /* starting */)); + assertTrue(pinnedStack.shouldBeVisible(null /* starting */)); + + final TestActivityStack fullscreenStack = createStackForShouldBeVisibleTest(display, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + // Home stack shouldn't be visible behind an opaque fullscreen stack, but pinned stack + // should be visible since it is always on-top. + fullscreenStack.setIsTranslucent(false); + assertFalse(homeStack.shouldBeVisible(null /* starting */)); + assertTrue(pinnedStack.shouldBeVisible(null /* starting */)); + assertTrue(fullscreenStack.shouldBeVisible(null /* starting */)); + + // Home stack should be visible behind a translucent fullscreen stack. + fullscreenStack.setIsTranslucent(true); + assertTrue(homeStack.shouldBeVisible(null /* starting */)); + assertTrue(pinnedStack.shouldBeVisible(null /* starting */)); + } + + @Test + public void testShouldBeVisible_SplitScreen() throws Exception { + final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay(); + final TestActivityStack homeStack = createStackForShouldBeVisibleTest(display, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); + final TestActivityStack splitScreenPrimary = createStackForShouldBeVisibleTest(display, + WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final TestActivityStack splitScreenSecondary = createStackForShouldBeVisibleTest(display, + WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */); + + // Home stack shouldn't be visible if both halves of split-screen are opaque. + splitScreenPrimary.setIsTranslucent(false); + splitScreenSecondary.setIsTranslucent(false); + assertFalse(homeStack.shouldBeVisible(null /* starting */)); + assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */)); + assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */)); + + // Home stack should be visible if one of the halves of split-screen is translucent. + splitScreenPrimary.setIsTranslucent(true); + assertTrue(homeStack.shouldBeVisible(null /* starting */)); + assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */)); + assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */)); + + final TestActivityStack splitScreenSecondary2 = createStackForShouldBeVisibleTest(display, + WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */); + // First split-screen secondary shouldn't be visible behind another opaque split-split + // secondary. + splitScreenSecondary2.setIsTranslucent(false); + assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */)); + assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */)); + + // First split-screen secondary should be visible behind another translucent split-split + // secondary. + splitScreenSecondary2.setIsTranslucent(true); + assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */)); + assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */)); + + final TestActivityStack assistantStack = createStackForShouldBeVisibleTest(display, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */); + + // Split-screen stacks shouldn't be visible behind an opaque fullscreen stack. + assistantStack.setIsTranslucent(false); + assertTrue(assistantStack.shouldBeVisible(null /* starting */)); + assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */)); + assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */)); + assertFalse(splitScreenSecondary2.shouldBeVisible(null /* starting */)); + + // Split-screen stacks should be visible behind a translucent fullscreen stack. + assistantStack.setIsTranslucent(true); + assertTrue(assistantStack.shouldBeVisible(null /* starting */)); + assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */)); + assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */)); + assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */)); + } + + @Test + public void testShouldBeVisible_Finishing() throws Exception { + final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay(); + final TestActivityStack homeStack = createStackForShouldBeVisibleTest(display, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); + final TestActivityStack translucentStack = createStackForShouldBeVisibleTest(display, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + translucentStack.setIsTranslucent(true); + + assertTrue(homeStack.shouldBeVisible(null /* starting */)); + assertTrue(translucentStack.shouldBeVisible(null /* starting */)); + + final ActivityRecord topRunningHomeActivity = homeStack.topRunningActivityLocked(); + topRunningHomeActivity.finishing = true; + final ActivityRecord topRunningTranslucentActivity = + translucentStack.topRunningActivityLocked(); + topRunningTranslucentActivity.finishing = true; + + // Home shouldn't be visible since its activity is marked as finishing and it isn't the top + // of the stack list. + assertFalse(homeStack.shouldBeVisible(null /* starting */)); + // Home should be visible if we are starting an activity within it. + assertTrue(homeStack.shouldBeVisible(topRunningHomeActivity /* starting */)); + // The translucent stack should be visible since it is the top of the stack list even though + // it has its activity marked as finishing. + assertTrue(translucentStack.shouldBeVisible(null /* starting */)); + } + + private <T extends ActivityStack> T createStackForShouldBeVisibleTest( + ActivityDisplay display, int windowingMode, int activityType, boolean onTop) { + final T stack = display.createStack(windowingMode, activityType, onTop); + // Create a task and activity in the stack so that it has a top running activity. + final TaskRecord task = createTask(mSupervisor, testActivityComponent, stack); + final ActivityRecord r = createActivity(mService, testActivityComponent, task, 0); + return stack; + } } diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java index 20077f3e94b0..f5cdf216f04a 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java @@ -52,12 +52,20 @@ import org.mockito.MockitoAnnotations; * A base class to handle common operations in activity related unit tests. */ public class ActivityTestsBase { + private static boolean sOneTimeSetupDone = false; + private final Context mContext = InstrumentationRegistry.getContext(); private HandlerThread mHandlerThread; @Before public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); + if (!sOneTimeSetupDone) { + sOneTimeSetupDone = true; + + // Allows to mock package local classes and methods + System.setProperty("dexmaker.share_classloader", "true"); + MockitoAnnotations.initMocks(this); + } mHandlerThread = new HandlerThread("ActivityTestsBaseThread"); mHandlerThread.start(); } @@ -268,20 +276,21 @@ public class ActivityTestsBase { return service; } - protected interface ActivityStackReporter { - int onActivityRemovedFromStackInvocationCount(); - } - /** * Overrided of {@link ActivityStack} that tracks test metrics, such as the number of times a * method is called. Note that its functionality depends on the implementations of the * construction arguments. */ protected static class TestActivityStack<T extends StackWindowController> - extends ActivityStack<T> implements ActivityStackReporter { + extends ActivityStack<T> { private int mOnActivityRemovedFromStackCount = 0; private T mContainerController; + static final int IS_TRANSLUCENT_UNSET = 0; + static final int IS_TRANSLUCENT_FALSE = 1; + static final int IS_TRANSLUCENT_TRUE = 2; + private int mIsTranslucent = IS_TRANSLUCENT_UNSET; + TestActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor, int windowingMode, int activityType, boolean onTop) { super(display, stackId, supervisor, windowingMode, activityType, onTop); @@ -294,8 +303,7 @@ public class ActivityTestsBase { } // Returns the number of times {@link #onActivityRemovedFromStack} has been called - @Override - public int onActivityRemovedFromStackInvocationCount() { + int onActivityRemovedFromStackInvocationCount() { return mOnActivityRemovedFromStackCount; } @@ -309,5 +317,22 @@ public class ActivityTestsBase { T getWindowContainerController() { return mContainerController; } + + void setIsTranslucent(boolean isTranslucent) { + mIsTranslucent = isTranslucent ? IS_TRANSLUCENT_TRUE : IS_TRANSLUCENT_FALSE; + } + + @Override + boolean isStackTranslucent(ActivityRecord starting, ActivityStack stackBehind) { + switch (mIsTranslucent) { + case IS_TRANSLUCENT_TRUE: + return true; + case IS_TRANSLUCENT_FALSE: + return false; + case IS_TRANSLUCENT_UNSET: + default: + return super.isStackTranslucent(starting, stackBehind); + } + } } } diff --git a/services/tests/servicestests/src/com/android/server/pm/UserDataPreparerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserDataPreparerTest.java index 7a676e258aba..bb35beb20a23 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserDataPreparerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserDataPreparerTest.java @@ -154,9 +154,6 @@ public class UserDataPreparerTest { File systemDeDir = mUserDataPreparer.getDataSystemDeDirectory(TEST_USER_ID); systemDeDir.mkdirs(); writeFile(new File(systemDeDir, "file"), "-----" ); - File miscDeDir = mUserDataPreparer.getDataMiscDeDirectory(TEST_USER_ID); - miscDeDir.mkdirs(); - writeFile(new File(miscDeDir, "file"), "-----" ); mUserDataPreparer.destroyUserData(TEST_USER_ID, StorageManager.FLAG_STORAGE_DE); @@ -168,8 +165,6 @@ public class UserDataPreparerTest { assertEquals(Collections.emptyList(), Arrays.asList(FileUtils.listFilesOrEmpty(systemDir))); assertEquals(Collections.emptyList(), Arrays.asList(FileUtils.listFilesOrEmpty( systemDeDir))); - assertEquals(Collections.emptyList(), Arrays.asList(FileUtils.listFilesOrEmpty( - miscDeDir))); } @Test @@ -177,9 +172,6 @@ public class UserDataPreparerTest { File systemCeDir = mUserDataPreparer.getDataSystemCeDirectory(TEST_USER_ID); systemCeDir.mkdirs(); writeFile(new File(systemCeDir, "file"), "-----" ); - File miscCeDir = mUserDataPreparer.getDataMiscCeDirectory(TEST_USER_ID); - miscCeDir.mkdirs(); - writeFile(new File(miscCeDir, "file"), "-----" ); mUserDataPreparer.destroyUserData(TEST_USER_ID, StorageManager.FLAG_STORAGE_CE); @@ -190,8 +182,6 @@ public class UserDataPreparerTest { assertEquals(Collections.emptyList(), Arrays.asList(FileUtils.listFilesOrEmpty( systemCeDir))); - assertEquals(Collections.emptyList(), Arrays.asList(FileUtils.listFilesOrEmpty( - miscCeDir))); } @Test diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java index b8e8946df6b4..0980f7ec4df0 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java @@ -63,7 +63,7 @@ import java.util.LinkedList; class WindowTestsBase { static WindowManagerService sWm = null; private static final IWindow sIWindow = new TestIWindow(); - private static final Session sMockSession = mock(Session.class); + private static Session sMockSession; // The default display is removed in {@link #setUp} and then we iterate over all displays to // make sure we don't collide with any existing display. If we run into no other display, the // added display should be treated as default. This cannot be the default display @@ -93,6 +93,7 @@ class WindowTestsBase { // Allows to mock package local classes and methods System.setProperty("dexmaker.share_classloader", "true"); MockitoAnnotations.initMocks(this); + sMockSession = mock(Session.class); } final Context context = InstrumentationRegistry.getTargetContext(); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index de980b2ffec2..99f8cfbf8063 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -1419,6 +1419,17 @@ public class CarrierConfigManager { "support_3gpp_call_forwarding_while_roaming_bool"; /** + * Boolean indicating whether to display voicemail number as default call forwarding number in + * call forwarding settings. + * If true, display vm number when cf number is null. + * If false, display the cf number from network. + * By default this value is false. + * @hide + */ + public static final String KEY_DISPLAY_VOICEMAIL_NUMBER_AS_DEFAULT_CALL_FORWARDING_NUMBER_BOOL = + "display_voicemail_number_as_default_call_forwarding_number"; + + /** * When {@code true}, the user will be notified when they attempt to place an international call * when the call is placed using wifi calling. * @hide @@ -1860,6 +1871,8 @@ public class CarrierConfigManager { sDefaults.putInt(KEY_EMERGENCY_NOTIFICATION_DELAY_INT, -1); sDefaults.putBoolean(KEY_ALLOW_USSD_REQUESTS_VIA_TELEPHONY_MANAGER_BOOL, true); sDefaults.putBoolean(KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL, true); + sDefaults.putBoolean(KEY_DISPLAY_VOICEMAIL_NUMBER_AS_DEFAULT_CALL_FORWARDING_NUMBER_BOOL, + false); sDefaults.putBoolean(KEY_NOTIFY_INTERNATIONAL_CALL_ON_WFC_BOOL, false); sDefaults.putStringArray(KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY, null); diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java index 6029995f2468..98195ada16f0 100644 --- a/telephony/java/android/telephony/SmsManager.java +++ b/telephony/java/android/telephony/SmsManager.java @@ -390,20 +390,23 @@ public final class SmsManager { * Inject an SMS PDU into the android application framework. * * <p>Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE} or carrier - * privileges. @see android.telephony.TelephonyManager#hasCarrierPrivileges + * privileges per {@link android.telephony.TelephonyManager#hasCarrierPrivileges}. * * @param pdu is the byte array of pdu to be injected into android application framework - * @param format is the format of SMS pdu (3gpp or 3gpp2) + * @param format is the format of SMS pdu ({@link SmsMessage#FORMAT_3GPP} or + * {@link SmsMessage#FORMAT_3GPP2}) * @param receivedIntent if not NULL this <code>PendingIntent</code> is * broadcast when the message is successfully received by the * android application framework, or failed. This intent is broadcasted at * the same time an SMS received from radio is acknowledged back. - * The result code will be <code>RESULT_SMS_HANDLED</code> for success, or - * <code>RESULT_SMS_GENERIC_ERROR</code> for error. + * The result code will be {@link android.provider.Telephony.Sms.Intents#RESULT_SMS_HANDLED} + * for success, or {@link android.provider.Telephony.Sms.Intents#RESULT_SMS_GENERIC_ERROR} for + * error. * - * @throws IllegalArgumentException if format is not one of 3gpp and 3gpp2. + * @throws IllegalArgumentException if the format is invalid. */ - public void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent) { + public void injectSmsPdu( + byte[] pdu, @SmsMessage.Format String format, PendingIntent receivedIntent) { if (!format.equals(SmsMessage.FORMAT_3GPP) && !format.equals(SmsMessage.FORMAT_3GPP2)) { // Format must be either 3gpp or 3gpp2. throw new IllegalArgumentException( diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java index dcdda8685e02..df41233559a1 100644 --- a/telephony/java/android/telephony/SmsMessage.java +++ b/telephony/java/android/telephony/SmsMessage.java @@ -16,24 +16,25 @@ package android.telephony; -import android.os.Binder; -import android.os.Parcel; +import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA; + +import android.annotation.StringDef; import android.content.res.Resources; +import android.os.Binder; import android.text.TextUtils; import com.android.internal.telephony.GsmAlphabet; import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails; +import com.android.internal.telephony.Sms7BitEncodingTranslator; import com.android.internal.telephony.SmsConstants; import com.android.internal.telephony.SmsMessageBase; import com.android.internal.telephony.SmsMessageBase.SubmitPduBase; -import com.android.internal.telephony.Sms7BitEncodingTranslator; -import java.lang.Math; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; -import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA; - /** * A Short Message Service message. @@ -81,15 +82,18 @@ public class SmsMessage { */ public static final int MAX_USER_DATA_SEPTETS_WITH_HEADER = 153; + /** @hide */ + @StringDef({FORMAT_3GPP, FORMAT_3GPP2}) + @Retention(RetentionPolicy.SOURCE) + public @interface Format {} + /** * Indicates a 3GPP format SMS message. - * @hide pending API council approval */ public static final String FORMAT_3GPP = "3gpp"; /** * Indicates a 3GPP2 format SMS message. - * @hide pending API council approval */ public static final String FORMAT_3GPP2 = "3gpp2"; diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java index 83ee361aa2e3..7b0703851441 100644 --- a/tests/net/java/com/android/server/IpSecServiceTest.java +++ b/tests/net/java/com/android/server/IpSecServiceTest.java @@ -21,6 +21,7 @@ import static android.system.OsConstants.EADDRINUSE; import static android.system.OsConstants.IPPROTO_UDP; import static android.system.OsConstants.SOCK_DGRAM; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; @@ -174,6 +175,7 @@ public class IpSecServiceTest { mIpSecService.openUdpEncapsulationSocket(0, new Binder()); assertNotNull(udpEncapResp); assertEquals(IpSecManager.Status.OK, udpEncapResp.status); + assertNotEquals(0, udpEncapResp.port); mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId); udpEncapResp.fileDescriptor.close(); } diff --git a/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockFindingClassVisitor.java b/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockFindingClassVisitor.java index a60f2a2019d2..ee0e36c9f76e 100644 --- a/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockFindingClassVisitor.java +++ b/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockFindingClassVisitor.java @@ -76,7 +76,7 @@ class LockFindingClassVisitor extends ClassVisitor { private MethodVisitor chain; public LockFindingMethodVisitor(String owner, MethodNode mn, MethodVisitor chain) { - super(Opcodes.ASM6, mn); + super(Utils.ASM_VERSION, mn); assert owner != null; this.owner = owner; this.chain = chain; diff --git a/tools/locked_region_code_injection/src/lockedregioncodeinjection/Utils.java b/tools/locked_region_code_injection/src/lockedregioncodeinjection/Utils.java index d2a2e7b90f2b..219c2b3a2fec 100644 --- a/tools/locked_region_code_injection/src/lockedregioncodeinjection/Utils.java +++ b/tools/locked_region_code_injection/src/lockedregioncodeinjection/Utils.java @@ -19,7 +19,7 @@ import org.objectweb.asm.Opcodes; public class Utils { - public static final int ASM_VERSION = Opcodes.ASM5; + public static final int ASM_VERSION = Opcodes.ASM6; /** * Reads a comma separated configuration similar to the Jack definition. diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl index a3a1054f869e..551e4df1c816 100644 --- a/wifi/java/android/net/wifi/IWifiManager.aidl +++ b/wifi/java/android/net/wifi/IWifiManager.aidl @@ -62,6 +62,8 @@ interface IWifiManager WifiConfiguration getMatchingWifiConfig(in ScanResult scanResult); + List<WifiConfiguration> getAllMatchingWifiConfigs(in ScanResult scanResult); + List<OsuProvider> getMatchingOsuProviders(in ScanResult scanResult); int addOrUpdateNetwork(in WifiConfiguration config); @@ -126,8 +128,6 @@ interface IWifiManager void releaseMulticastLock(); - void setWifiApEnabled(in WifiConfiguration wifiConfig, boolean enable); - void updateInterfaceIpState(String ifaceName, int mode); boolean startSoftAp(in WifiConfiguration wifiConfig); diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index b08b4b7c71b6..b72df0722484 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -1029,6 +1029,26 @@ public class WifiManager { } /** + * Return all matching WifiConfigurations for this ScanResult. + * + * An empty list will be returned when no configurations are installed or if no configurations + * match the ScanResult. + * + * @param scanResult scanResult that represents the BSSID + * @return A list of {@link WifiConfiguration} + * @throws UnsupportedOperationException if Passpoint is not enabled on the device. + * @hide + */ + public List<WifiConfiguration> getAllMatchingWifiConfigs(ScanResult scanResult) { + try { + return mService.getAllMatchingWifiConfigs(scanResult); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + + /** * Returns a list of Hotspot 2.0 OSU (Online Sign-Up) providers associated with the given AP. * * An empty list will be returned if no match is found. @@ -1857,32 +1877,6 @@ public class WifiManager { } /** - * This call is deprecated and removed. It is no longer used to - * start WiFi Tethering. Please use {@link ConnectivityManager#startTethering(int, boolean, - * ConnectivityManager#OnStartTetheringCallback)} if - * the caller has proper permissions. Callers can also use the LocalOnlyHotspot feature for a - * hotspot capable of communicating with co-located devices {@link - * WifiManager#startLocalOnlyHotspot(LocalOnlyHotspotCallback)}. - * - * @param wifiConfig SSID, security and channel details as - * part of WifiConfiguration - * @return {@code false} - * - * @hide - * @deprecated This API is nolonger supported. - * @removed - */ - @SystemApi - @Deprecated - @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) - public boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) { - String packageName = mContext.getOpPackageName(); - - Log.w(TAG, packageName + " attempted call to setWifiApEnabled: enabled = " + enabled); - return false; - } - - /** * Call allowing ConnectivityService to update WifiService with interface mode changes. * * The possible modes include: {@link IFACE_IP_MODE_TETHERED}, diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java index b235ccc7a89e..ee6f12b775a8 100644 --- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java +++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java @@ -777,14 +777,4 @@ public class WifiManagerTest { mWifiManager.unregisterLocalOnlyHotspotObserver(); verify(mWifiService).stopWatchLocalOnlyHotspot(); } - - /** - * Verify that calls to setWifiApEnabled return false. - */ - @Test - public void testSetWifiApEnabledReturnsFalse() throws Exception { - assertFalse(mWifiManager.setWifiApEnabled(null, true)); - assertFalse(mWifiManager.setWifiApEnabled(null, false)); - verify(mWifiService, never()).setWifiApEnabled(any(), anyBoolean()); - } } |