summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp2
-rw-r--r--Android.mk5
-rw-r--r--api/current.txt2
-rw-r--r--api/system-current.txt2
-rw-r--r--api/system-removed.txt1
-rw-r--r--api/test-current.txt9
-rw-r--r--cmds/incidentd/tests/Reporter_test.cpp14
-rw-r--r--cmds/statsd/Android.mk5
-rw-r--r--cmds/statsd/src/StatsLogProcessor.cpp40
-rw-r--r--cmds/statsd/src/StatsLogProcessor.h33
-rw-r--r--cmds/statsd/src/StatsService.cpp38
-rw-r--r--cmds/statsd/src/StatsService.h32
-rw-r--r--cmds/statsd/src/condition/SimpleConditionTracker.cpp2
-rw-r--r--cmds/statsd/src/config/ConfigManager.cpp19
-rw-r--r--cmds/statsd/src/logd/LogEvent.cpp22
-rw-r--r--cmds/statsd/src/logd/LogEvent.h8
-rw-r--r--cmds/statsd/src/metrics/CountAnomalyTracker.cpp56
-rw-r--r--cmds/statsd/src/metrics/CountAnomalyTracker.h14
-rw-r--r--cmds/statsd/src/metrics/CountMetricProducer.cpp25
-rw-r--r--cmds/statsd/src/metrics/CountMetricProducer.h4
-rw-r--r--cmds/statsd/src/metrics/EventMetricProducer.cpp132
-rw-r--r--cmds/statsd/src/metrics/EventMetricProducer.h70
-rw-r--r--cmds/statsd/src/metrics/MetricsManager.cpp2
-rw-r--r--cmds/statsd/src/metrics/MetricsManager.h10
-rw-r--r--cmds/statsd/src/metrics/metrics_manager_util.cpp178
-rw-r--r--cmds/statsd/src/metrics/metrics_manager_util.h5
-rw-r--r--cmds/statsd/src/stats_events.proto355
-rw-r--r--cmds/statsd/src/stats_util.h8
-rw-r--r--cmds/statsd/src/statsd_config.proto2
-rw-r--r--config/compiled-classes-phone12
-rw-r--r--core/java/Android.bp4
-rw-r--r--core/java/android/app/slice/SliceProvider.java43
-rw-r--r--core/java/android/bluetooth/BluetoothAdapter.java46
-rw-r--r--core/java/android/bluetooth/BluetoothHidDevice.java (renamed from core/java/android/bluetooth/BluetoothInputHost.java)44
-rw-r--r--core/java/android/bluetooth/BluetoothHidHost.java (renamed from core/java/android/bluetooth/BluetoothInputDevice.java)70
-rw-r--r--core/java/android/bluetooth/BluetoothProfile.java8
-rw-r--r--core/java/android/content/pm/ApplicationInfo.java2
-rw-r--r--core/java/android/content/pm/PackageManagerInternal.java3
-rw-r--r--core/java/android/hardware/usb/UsbManager.java5
-rw-r--r--core/java/android/inputmethodservice/IInputMethodWrapper.java42
-rw-r--r--core/java/android/net/metrics/ConnectStats.java11
-rw-r--r--core/java/android/net/metrics/DnsEvent.java21
-rw-r--r--core/java/android/net/metrics/NetworkMetrics.java168
-rw-r--r--core/java/android/os/HwBinder.java3
-rw-r--r--core/java/android/os/IStatsCallbacks.aidl25
-rw-r--r--core/java/android/os/IStatsManager.aidl13
-rw-r--r--core/java/android/os/StrictMode.java201
-rwxr-xr-xcore/java/android/provider/Settings.java9
-rw-r--r--core/java/android/security/NetworkSecurityPolicy.java4
-rw-r--r--core/java/android/security/net/config/NetworkSecurityConfig.java6
-rw-r--r--core/java/android/text/Layout.java2
-rw-r--r--core/java/android/text/MeasuredText.java43
-rw-r--r--core/java/android/text/StaticLayout.java613
-rw-r--r--core/java/android/text/TextLine.java38
-rw-r--r--core/java/android/text/TextUtils.java4
-rw-r--r--core/java/android/view/textclassifier/TextClassification.java46
-rw-r--r--core/java/android/view/textclassifier/TextClassifier.java8
-rw-r--r--core/java/android/webkit/WebViewFactory.java28
-rw-r--r--core/java/android/webkit/WebViewLibraryLoader.java25
-rw-r--r--core/java/android/widget/Editor.java162
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java88
-rw-r--r--core/java/com/android/internal/view/InputConnectionWrapper.java41
-rw-r--r--core/java/com/android/internal/widget/Magnifier.java40
-rw-r--r--core/jni/android_os_HwBinder.cpp8
-rw-r--r--core/jni/android_text_StaticLayout.cpp294
-rw-r--r--core/res/AndroidManifest.xml10
-rw-r--r--core/res/res/layout/magnifier.xml7
-rw-r--r--core/res/res/values/dimens.xml1
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerStressTestRunner.java11
-rw-r--r--core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java6
-rw-r--r--core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerUnitTestRunner.java2
-rw-r--r--core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java125
-rw-r--r--core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiSoftAPTest.java78
-rw-r--r--core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java6
-rw-r--r--core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java34
-rw-r--r--core/tests/coretests/src/android/text/TextLineTest.java67
-rw-r--r--data/etc/privapp-permissions-platform.xml6
-rw-r--r--libs/protoutil/include/android/util/ProtoOutputStream.h4
-rw-r--r--libs/protoutil/src/ProtoOutputStream.cpp5
-rw-r--r--packages/EasterEgg/src/com/android/egg/octo/Ocquarium.java28
-rw-r--r--packages/SettingsLib/OWNERS21
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/Utils.java7
-rwxr-xr-xpackages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java10
-rwxr-xr-xpackages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java4
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSimStatusImeiInfoPreferenceController.java36
-rwxr-xr-xpackages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java8
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/inputmethod/OWNERS5
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java120
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java85
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java7
-rw-r--r--services/core/java/com/android/server/IpSecService.java6
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java12
-rw-r--r--services/core/java/com/android/server/am/ActivityDisplay.java168
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java12
-rw-r--r--services/core/java/com/android/server/am/ActivityRecord.java16
-rw-r--r--services/core/java/com/android/server/am/ActivityStack.java187
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java169
-rw-r--r--services/core/java/com/android/server/am/AppTaskImpl.java10
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java12
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.java21
-rw-r--r--services/core/java/com/android/server/am/TaskRecord.java5
-rw-r--r--services/core/java/com/android/server/connectivity/NetdEventListenerService.java171
-rw-r--r--services/core/java/com/android/server/location/GnssLocationProvider.java8
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java165
-rw-r--r--services/core/java/com/android/server/pm/UserDataPreparer.java2
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java17
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java202
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionSettings.java9
-rw-r--r--services/core/java/com/android/server/wm/DragState.java196
-rw-r--r--services/core/java/com/android/server/wm/Session.java4
-rw-r--r--services/core/java/com/android/server/wm/WindowAnimator.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java17
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java125
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java41
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserDataPreparerTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java3
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java13
-rw-r--r--telephony/java/android/telephony/SmsManager.java15
-rw-r--r--telephony/java/android/telephony/SmsMessage.java20
-rw-r--r--tests/net/java/com/android/server/IpSecServiceTest.java2
-rw-r--r--tools/locked_region_code_injection/src/lockedregioncodeinjection/LockFindingClassVisitor.java2
-rw-r--r--tools/locked_region_code_injection/src/lockedregioncodeinjection/Utils.java2
-rw-r--r--wifi/java/android/net/wifi/IWifiManager.aidl4
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java46
-rw-r--r--wifi/tests/src/android/net/wifi/WifiManagerTest.java10
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=&lt;profile&gt;, device=&lt;device&gt;)"
*/
@@ -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=&lt;profile&gt;, device=&lt;device&gt;)"
*/
@@ -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());
- }
}