summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmds/statsd/src/StatsService.cpp72
-rw-r--r--cmds/statsd/src/StatsService.h7
-rw-r--r--cmds/statsd/src/logd/LogEvent.cpp3
-rw-r--r--packages/NetworkPermissionConfig/AndroidManifest.xml2
-rw-r--r--packages/NetworkStack/src/android/net/apf/ApfFilter.java17
-rw-r--r--packages/NetworkStack/src/android/net/ip/IpClient.java34
-rw-r--r--packages/NetworkStack/tests/src/android/net/apf/ApfTest.java8
-rw-r--r--packages/SystemUI/res/layout/contextual.xml14
-rw-r--r--packages/SystemUI/res/layout/rotate_suggestion.xml29
-rw-r--r--packages/SystemUI/res/layout/start_contextual.xml36
-rw-r--r--packages/SystemUI/res/values/config.xml2
-rw-r--r--packages/SystemUI/res/values/strings.xml1
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl5
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButtonGroup.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java87
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java54
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarContextTest.java22
-rw-r--r--services/core/java/com/android/server/attention/AttentionManagerService.java209
-rw-r--r--services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java16
-rw-r--r--services/core/java/com/android/server/wm/ActivityStack.java2
-rw-r--r--services/net/java/android/net/ip/IIpClient.aidl2
-rw-r--r--services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerLogger.java136
-rw-r--r--services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java198
-rwxr-xr-xtelephony/java/android/telephony/CarrierConfigManager.java3
-rw-r--r--tests/RollbackTest/Android.bp10
-rw-r--r--tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java85
-rw-r--r--tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java43
-rw-r--r--tests/RollbackTest/TestApex/RollbackTestApexV3.json4
-rw-r--r--tests/net/common/Android.bp2
-rw-r--r--tests/net/common/java/android/net/LinkPropertiesTest.java4
-rw-r--r--tests/net/common/java/android/net/NetworkCapabilitiesTest.java18
-rw-r--r--tests/net/common/java/android/net/NetworkTest.java8
-rw-r--r--tests/net/common/java/android/net/StaticIpConfigurationTest.java58
-rw-r--r--tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java2
-rw-r--r--tests/net/common/java/android/net/metrics/DhcpErrorEventTest.kt67
-rw-r--r--tests/net/java/android/net/TcpKeepalivePacketDataTest.java2
-rw-r--r--tests/net/java/com/android/server/ConnectivityServiceTest.java10
-rw-r--r--tests/net/util/java/com/android/internal/util/TestUtils.java25
-rw-r--r--tools/apilint/apilint.py4
45 files changed, 1196 insertions, 184 deletions
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 14332528e992..4c97c3454e82 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -1202,9 +1202,10 @@ Status StatsService::unregisterPullerCallback(int32_t atomTag, const String16& p
return Status::ok();
}
-Status StatsService::sendBinaryPushStateChangedAtom(const android::String16& trainName,
- int64_t trainVersionCode, int options,
- int32_t state,
+Status StatsService::sendBinaryPushStateChangedAtom(const android::String16& trainNameIn,
+ const int64_t trainVersionCodeIn,
+ const int options,
+ const int32_t state,
const std::vector<int64_t>& experimentIdsIn) {
uid_t uid = IPCThreadState::self()->getCallingUid();
// For testing
@@ -1224,34 +1225,64 @@ Status StatsService::sendBinaryPushStateChangedAtom(const android::String16& tra
// TODO: add verifier permission
bool readTrainInfoSuccess = false;
- InstallTrainInfo trainInfo;
- if (trainVersionCode == -1 || experimentIdsIn.empty() || trainName.size() == 0) {
- readTrainInfoSuccess = StorageManager::readTrainInfo(trainInfo);
- }
+ InstallTrainInfo trainInfoOnDisk;
+ readTrainInfoSuccess = StorageManager::readTrainInfo(trainInfoOnDisk);
+
+ bool resetExperimentIds = false;
+ int64_t trainVersionCode = trainVersionCodeIn;
+ std::string trainNameUtf8 = std::string(String8(trainNameIn).string());
+ if (readTrainInfoSuccess) {
+ // Keep the old train version if we received an empty version.
+ if (trainVersionCodeIn == -1) {
+ trainVersionCode = trainInfoOnDisk.trainVersionCode;
+ } else if (trainVersionCodeIn != trainInfoOnDisk.trainVersionCode) {
+ // Reset experiment ids if we receive a new non-empty train version.
+ resetExperimentIds = true;
+ }
+
+ // Keep the old train name if we received an empty train name.
+ if (trainNameUtf8.size() == 0) {
+ trainNameUtf8 = trainInfoOnDisk.trainName;
+ } else if (trainNameUtf8 != trainInfoOnDisk.trainName) {
+ // Reset experiment ids if we received a new valid train name.
+ resetExperimentIds = true;
+ }
- if (trainVersionCode == -1 && readTrainInfoSuccess) {
- trainVersionCode = trainInfo.trainVersionCode;
+ // Reset if we received a different experiment id.
+ if (!experimentIdsIn.empty() &&
+ (trainInfoOnDisk.experimentIds.empty() ||
+ experimentIdsIn[0] != trainInfoOnDisk.experimentIds[0])) {
+ resetExperimentIds = true;
+ }
}
// Find the right experiment IDs
std::vector<int64_t> experimentIds;
- if (readTrainInfoSuccess && experimentIdsIn.empty()) {
- experimentIds = trainInfo.experimentIds;
- } else {
+ if (resetExperimentIds || !readTrainInfoSuccess) {
experimentIds = experimentIdsIn;
+ } else {
+ experimentIds = trainInfoOnDisk.experimentIds;
+ }
+
+ if (!experimentIds.empty()) {
+ int64_t firstId = experimentIds[0];
+ switch (state) {
+ case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALL_SUCCESS:
+ experimentIds.push_back(firstId + 1);
+ break;
+ case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_INITIATED:
+ experimentIds.push_back(firstId + 2);
+ break;
+ case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_SUCCESS:
+ experimentIds.push_back(firstId + 3);
+ break;
+ }
}
// Flatten the experiment IDs to proto
vector<uint8_t> experimentIdsProtoBuffer;
writeExperimentIdsToProto(experimentIds, &experimentIdsProtoBuffer);
-
- // Find the right train name
- std::string trainNameUtf8;
- if (readTrainInfoSuccess && trainName.size() == 0) {
- trainNameUtf8 = trainInfo.trainName;
- } else {
- trainNameUtf8 = std::string(String8(trainName).string());
- }
+ StorageManager::writeTrainInfo(trainVersionCode, trainNameUtf8, state, experimentIds);
userid_t userId = multiuser_get_user_id(uid);
bool requiresStaging = options & IStatsManager::FLAG_REQUIRE_STAGING;
@@ -1260,7 +1291,6 @@ Status StatsService::sendBinaryPushStateChangedAtom(const android::String16& tra
LogEvent event(trainNameUtf8, trainVersionCode, requiresStaging, rollbackEnabled,
requiresLowLatencyMonitor, state, experimentIdsProtoBuffer, userId);
mProcessor->OnLogEvent(&event);
- StorageManager::writeTrainInfo(trainVersionCode, trainNameUtf8, state, experimentIds);
return Status::ok();
}
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 929d260dcd95..0b6df8b0997c 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -189,8 +189,11 @@ public:
* Binder call to log BinaryPushStateChanged atom.
*/
virtual Status sendBinaryPushStateChangedAtom(
- const android::String16& trainName, int64_t trainVersionCode, int options,
- int32_t state, const std::vector<int64_t>& experimentIds) override;
+ const android::String16& trainNameIn,
+ const int64_t trainVersionCodeIn,
+ const int options,
+ const int32_t state,
+ const std::vector<int64_t>& experimentIdsIn) override;
/**
* Binder call to get registered experiment IDs.
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index ca874b57170e..0ade53118d77 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -678,7 +678,8 @@ void LogEvent::ToProto(ProtoOutputStream& protoOutput) const {
writeFieldValueTreeToStream(mTagId, getValues(), &protoOutput);
}
-void writeExperimentIdsToProto(const std::vector<int64_t>& experimentIds, std::vector<uint8_t>* protoOut) {
+void writeExperimentIdsToProto(const std::vector<int64_t>& experimentIds,
+ std::vector<uint8_t>* protoOut) {
ProtoOutputStream proto;
for (const auto& expId : experimentIds) {
proto.write(FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED | FIELD_ID_EXPERIMENT_ID,
diff --git a/packages/NetworkPermissionConfig/AndroidManifest.xml b/packages/NetworkPermissionConfig/AndroidManifest.xml
index 34f987c8f0d4..496fa4defcde 100644
--- a/packages/NetworkPermissionConfig/AndroidManifest.xml
+++ b/packages/NetworkPermissionConfig/AndroidManifest.xml
@@ -19,7 +19,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.networkstack.permissionconfig"
android:sharedUserId="android.uid.networkstack"
- android:versionCode="11"
+ android:versionCode="210000000"
android:versionName="Q-initial">
<uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" />
<!--
diff --git a/packages/NetworkStack/src/android/net/apf/ApfFilter.java b/packages/NetworkStack/src/android/net/apf/ApfFilter.java
index 663e2f10ffe2..359c85983a94 100644
--- a/packages/NetworkStack/src/android/net/apf/ApfFilter.java
+++ b/packages/NetworkStack/src/android/net/apf/ApfFilter.java
@@ -39,6 +39,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.net.LinkAddress;
import android.net.LinkProperties;
+import android.net.NattKeepalivePacketDataParcelable;
import android.net.TcpKeepalivePacketDataParcelable;
import android.net.apf.ApfGenerator.IllegalInstructionException;
import android.net.apf.ApfGenerator.Register;
@@ -1691,13 +1692,13 @@ public class ApfFilter {
}
/**
- * Add keepalive ack packet filter.
+ * Add TCP keepalive ack packet filter.
* This will add a filter to drop acks to the keepalive packet passed as an argument.
*
* @param slot The index used to access the filter.
* @param sentKeepalivePacket The attributes of the sent keepalive packet.
*/
- public synchronized void addKeepalivePacketFilter(final int slot,
+ public synchronized void addTcpKeepalivePacketFilter(final int slot,
final TcpKeepalivePacketDataParcelable sentKeepalivePacket) {
log("Adding keepalive ack(" + slot + ")");
if (null != mKeepaliveAcks.get(slot)) {
@@ -1711,6 +1712,18 @@ public class ApfFilter {
}
/**
+ * Add NATT keepalive packet filter.
+ * This will add a filter to drop NATT keepalive packet which is passed as an argument.
+ *
+ * @param slot The index used to access the filter.
+ * @param sentKeepalivePacket The attributes of the sent keepalive packet.
+ */
+ public synchronized void addNattKeepalivePacketFilter(final int slot,
+ final NattKeepalivePacketDataParcelable sentKeepalivePacket) {
+ Log.e(TAG, "APF add NATT keepalive filter is not implemented");
+ }
+
+ /**
* Remove keepalive packet filter.
*
* @param slot The index used to access the filter.
diff --git a/packages/NetworkStack/src/android/net/ip/IpClient.java b/packages/NetworkStack/src/android/net/ip/IpClient.java
index 96e09fafb6b6..dc74c041c35a 100644
--- a/packages/NetworkStack/src/android/net/ip/IpClient.java
+++ b/packages/NetworkStack/src/android/net/ip/IpClient.java
@@ -29,6 +29,7 @@ import android.net.INetd;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
+import android.net.NattKeepalivePacketDataParcelable;
import android.net.NetworkStackIpMemoryStore;
import android.net.ProvisioningConfigurationParcelable;
import android.net.ProxyInfo;
@@ -371,6 +372,10 @@ public class IpClient extends StateMachine {
private boolean mMulticastFiltering;
private long mStartTimeMillis;
+ /* This must match the definition in KeepaliveTracker.KeepaliveInfo */
+ private static final int TYPE_NATT = 1;
+ private static final int TYPE_TCP = 2;
+
/**
* Reading the snapshot is an asynchronous operation initiated by invoking
* Callback.startReadPacketFilter() and completed when the WiFi Service responds with an
@@ -553,6 +558,11 @@ public class IpClient extends StateMachine {
IpClient.this.addKeepalivePacketFilter(slot, pkt);
}
@Override
+ public void addNattKeepalivePacketFilter(int slot, NattKeepalivePacketDataParcelable pkt) {
+ checkNetworkStackCallingPermission();
+ IpClient.this.addNattKeepalivePacketFilter(slot, pkt);
+ }
+ @Override
public void removeKeepalivePacketFilter(int slot) {
checkNetworkStackCallingPermission();
IpClient.this.removeKeepalivePacketFilter(slot);
@@ -691,11 +701,20 @@ public class IpClient extends StateMachine {
}
/**
- * Called by WifiStateMachine to add keepalive packet filter before setting up
+ * Called by WifiStateMachine to add TCP keepalive packet filter before setting up
* keepalive offload.
*/
public void addKeepalivePacketFilter(int slot, @NonNull TcpKeepalivePacketDataParcelable pkt) {
- sendMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF, slot, 0 /* Unused */, pkt);
+ sendMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF, slot, TYPE_TCP, pkt);
+ }
+
+ /**
+ * Called by WifiStateMachine to add NATT keepalive packet filter before setting up
+ * keepalive offload.
+ */
+ public void addNattKeepalivePacketFilter(int slot,
+ @NonNull NattKeepalivePacketDataParcelable pkt) {
+ sendMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF, slot, TYPE_NATT, pkt);
}
/**
@@ -1607,9 +1626,16 @@ public class IpClient extends StateMachine {
case CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF: {
final int slot = msg.arg1;
+ final int type = msg.arg2;
+
if (mApfFilter != null) {
- mApfFilter.addKeepalivePacketFilter(slot,
- (TcpKeepalivePacketDataParcelable) msg.obj);
+ if (type == TYPE_NATT) {
+ mApfFilter.addNattKeepalivePacketFilter(slot,
+ (NattKeepalivePacketDataParcelable) msg.obj);
+ } else {
+ mApfFilter.addTcpKeepalivePacketFilter(slot,
+ (TcpKeepalivePacketDataParcelable) msg.obj);
+ }
}
break;
}
diff --git a/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java b/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java
index a0e508f130a5..93ab3be28fc7 100644
--- a/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java
+++ b/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java
@@ -1553,7 +1553,7 @@ public class ApfTest {
parcel.seq = seqNum;
parcel.ack = ackNum;
- apfFilter.addKeepalivePacketFilter(slot1, parcel);
+ apfFilter.addTcpKeepalivePacketFilter(slot1, parcel);
program = cb.getApfProgram();
// Verify IPv4 keepalive ack packet is dropped
@@ -1592,7 +1592,7 @@ public class ApfTest {
ipv6Parcel.seq = seqNum;
ipv6Parcel.ack = ackNum;
- apfFilter.addKeepalivePacketFilter(slot1, ipv6Parcel);
+ apfFilter.addTcpKeepalivePacketFilter(slot1, ipv6Parcel);
program = cb.getApfProgram();
// Verify IPv6 keepalive ack packet is dropped
@@ -1614,8 +1614,8 @@ public class ApfTest {
apfFilter.removeKeepalivePacketFilter(slot1);
// Verify multiple filters
- apfFilter.addKeepalivePacketFilter(slot1, parcel);
- apfFilter.addKeepalivePacketFilter(slot2, ipv6Parcel);
+ apfFilter.addTcpKeepalivePacketFilter(slot1, parcel);
+ apfFilter.addTcpKeepalivePacketFilter(slot2, ipv6Parcel);
program = cb.getApfProgram();
// Verify IPv4 keepalive ack packet is dropped
diff --git a/packages/SystemUI/res/layout/contextual.xml b/packages/SystemUI/res/layout/contextual.xml
index 9b6ccaeef1e4..90a776884699 100644
--- a/packages/SystemUI/res/layout/contextual.xml
+++ b/packages/SystemUI/res/layout/contextual.xml
@@ -42,16 +42,10 @@
android:layout_height="match_parent"
android:visibility="invisible"
/>
- <com.android.systemui.statusbar.policy.KeyButtonView
- android:id="@+id/rotate_suggestion"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_weight="0"
- android:scaleType="center"
- android:visibility="invisible"
- android:contentDescription="@string/accessibility_rotate_button"
- android:paddingStart="@dimen/navigation_key_padding"
- android:paddingEnd="@dimen/navigation_key_padding"
+ <include layout="@layout/rotate_suggestion"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="invisible"
/>
<com.android.systemui.statusbar.policy.KeyButtonView
android:id="@+id/accessibility_button"
diff --git a/packages/SystemUI/res/layout/rotate_suggestion.xml b/packages/SystemUI/res/layout/rotate_suggestion.xml
new file mode 100644
index 000000000000..d7f67db0390c
--- /dev/null
+++ b/packages/SystemUI/res/layout/rotate_suggestion.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 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
+ -->
+
+<com.android.systemui.statusbar.policy.KeyButtonView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/rotate_suggestion"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_weight="0"
+ android:scaleType="center"
+ android:visibility="invisible"
+ android:contentDescription="@string/accessibility_rotate_button"
+ android:paddingStart="@dimen/navigation_key_padding"
+ android:paddingEnd="@dimen/navigation_key_padding"
+/> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/start_contextual.xml b/packages/SystemUI/res/layout/start_contextual.xml
new file mode 100644
index 000000000000..e022c7301e3b
--- /dev/null
+++ b/packages/SystemUI/res/layout/start_contextual.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:systemui="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/start_menu_container"
+ android:layout_width="@dimen/navigation_key_width"
+ android:layout_height="match_parent"
+ android:importantForAccessibility="no"
+ android:focusable="false"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ >
+ <include layout="@layout/rotate_suggestion"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="invisible"
+ />
+ <include layout="@layout/back"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="invisible"
+ />
+</FrameLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 73386879a20d..4abe9f0bfb5c 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -331,7 +331,7 @@
<!-- Nav bar button default ordering/layout -->
<string name="config_navBarLayout" translatable="false">left[.5W],back[1WC];home;recent[1WC],right[.5W]</string>
<string name="config_navBarLayoutQuickstep" translatable="false">back[1.7WC];home;contextual[1.7WC]</string>
- <string name="config_navBarLayoutHandle" translatable="false">back[1.7WC];home_handle;ime_switcher[1.7WC]</string>
+ <string name="config_navBarLayoutHandle" translatable="false">start_contextual[.1WC];home_handle;ime_switcher[.1WC]</string>
<bool name="quick_settings_show_full_alarm">false</bool>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index acc03c43d34c..60d71260058f 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1345,6 +1345,7 @@
<!-- Screen pinning dialog description. -->
<string name="screen_pinning_description">This keeps it in view until you unpin. Touch &amp; hold Back and Overview to unpin.</string>
<string name="screen_pinning_description_recents_invisible">This keeps it in view until you unpin. Touch &amp; hold Back and Home to unpin.</string>
+ <string name="screen_pinning_description_gestural">This keeps it in view until you unpin. Swipe up &amp; hold to unpin.</string>
<!-- Screen pinning dialog description. -->
<string name="screen_pinning_description_accessible">This keeps it in view until you unpin. Touch &amp; hold Overview to unpin.</string>
<string name="screen_pinning_description_recents_invisible_accessible">This keeps it in view until you unpin. Touch &amp; hold Home to unpin.</string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index 6f44623eb320..2c5fa60d3516 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -96,4 +96,9 @@ interface ISystemUiProxy {
* Notifies that the accessibility button in the system's navigation area has been long clicked
*/
void notifyAccessibilityButtonLongClicked() = 16;
+
+ /**
+ * Ends the system screen pinning.
+ */
+ void stopScreenPinning() = 17;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 3ace7050c743..5ed6a429cc2f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -35,6 +35,7 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_N
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
import android.annotation.FloatRange;
+import android.app.ActivityTaskManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -151,6 +152,25 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
}
@Override
+ public void stopScreenPinning() {
+ if (!verifyCaller("stopScreenPinning")) {
+ return;
+ }
+ long token = Binder.clearCallingIdentity();
+ try {
+ mHandler.post(() -> {
+ try {
+ ActivityTaskManager.getService().stopSystemLockTaskMode();
+ } catch (RemoteException e) {
+ Log.e(TAG_OPS, "Failed to stop screen pinning");
+ }
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
public void onStatusBarMotionEvent(MotionEvent event) {
if (!verifyCaller("onStatusBarMotionEvent")) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 07391ed5867f..ade903d7e272 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -48,14 +48,17 @@ import android.widget.TextView;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SysUiServiceProvider;
+import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.systemui.statusbar.phone.NavigationBarView;
+import com.android.systemui.statusbar.phone.NavigationModeController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.util.leak.RotationUtils;
import java.util.ArrayList;
-public class ScreenPinningRequest implements View.OnClickListener {
+public class ScreenPinningRequest implements View.OnClickListener,
+ NavigationModeController.ModeChangedListener {
private final Context mContext;
@@ -64,6 +67,7 @@ public class ScreenPinningRequest implements View.OnClickListener {
private final OverviewProxyService mOverviewProxyService;
private RequestWindowView mRequestWindow;
+ private int mNavBarMode;
// Id of task to be pinned or locked.
private int taskId;
@@ -75,6 +79,7 @@ public class ScreenPinningRequest implements View.OnClickListener {
mWindowManager = (WindowManager)
mContext.getSystemService(Context.WINDOW_SERVICE);
mOverviewProxyService = Dependency.get(OverviewProxyService.class);
+ mNavBarMode = Dependency.get(NavigationModeController.class).addListener(this);
}
public void clearPrompt() {
@@ -103,6 +108,11 @@ public class ScreenPinningRequest implements View.OnClickListener {
mWindowManager.addView(mRequestWindow, lp);
}
+ @Override
+ public void onNavigationModeChanged(int mode) {
+ mNavBarMode = mode;
+ }
+
public void onConfigurationChanged() {
if (mRequestWindow != null) {
mRequestWindow.onConfigurationChanged();
@@ -224,7 +234,9 @@ public class ScreenPinningRequest implements View.OnClickListener {
mLayout.findViewById(R.id.screen_pinning_text_area)
.setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE);
View buttons = mLayout.findViewById(R.id.screen_pinning_buttons);
- if (WindowManagerWrapper.getInstance().hasSoftNavigationBar(mContext.getDisplayId())) {
+ WindowManagerWrapper wm = WindowManagerWrapper.getInstance();
+ if (!QuickStepContract.isGesturalMode(mNavBarMode)
+ && wm.hasSoftNavigationBar(mContext.getDisplayId())) {
buttons.setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE);
swapChildrenIfRtlAndVertical(buttons);
} else {
@@ -248,7 +260,9 @@ public class ScreenPinningRequest implements View.OnClickListener {
&& navigationBarView.isRecentsButtonVisible();
boolean touchExplorationEnabled = mAccessibilityService.isTouchExplorationEnabled();
int descriptionStringResId;
- if (recentsVisible) {
+ if (QuickStepContract.isGesturalMode(mNavBarMode)) {
+ descriptionStringResId = R.string.screen_pinning_description_gestural;
+ } else if (recentsVisible) {
mLayout.findViewById(R.id.screen_pinning_recents_group).setVisibility(VISIBLE);
mLayout.findViewById(R.id.screen_pinning_home_bg_light).setVisibility(INVISIBLE);
mLayout.findViewById(R.id.screen_pinning_home_bg).setVisibility(INVISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
index 5f5fad33fbfe..6a93c7c9e5c4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
@@ -47,7 +47,7 @@ public class ButtonDispatcher {
private Boolean mLongClickable;
private Float mAlpha;
private Float mDarkIntensity;
- private Integer mVisibility = -1;
+ private Integer mVisibility = View.VISIBLE;
private Boolean mDelayTouchFeedback;
private KeyButtonDrawable mImageDrawable;
private View mCurrentView;
@@ -86,7 +86,7 @@ public class ButtonDispatcher {
if (mAlpha != null) {
view.setAlpha(mAlpha);
}
- if (mVisibility != null && mVisibility != -1) {
+ if (mVisibility != null) {
view.setVisibility(mVisibility);
}
if (mAccessibilityDelegate != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java
index 541c142c422a..5bc17f5bc2c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java
@@ -51,7 +51,7 @@ public class ContextualButton extends ButtonDispatcher {
* Reload the drawable from resource id, should reapply the previous dark intensity.
*/
public void updateIcon() {
- if (getCurrentView() == null || !getCurrentView().isAttachedToWindow()) {
+ if (getCurrentView() == null || !getCurrentView().isAttachedToWindow() || mIconResId == 0) {
return;
}
final KeyButtonDrawable currentDrawable = getImageDrawable();
@@ -92,7 +92,7 @@ public class ContextualButton extends ButtonDispatcher {
setVisibility(View.VISIBLE);
return true;
}
- return mGroup.setButtonVisiblity(getId(), true /* visible */) == View.VISIBLE;
+ return mGroup.setButtonVisibility(getId(), true /* visible */) == View.VISIBLE;
}
/**
@@ -104,7 +104,7 @@ public class ContextualButton extends ButtonDispatcher {
setVisibility(View.INVISIBLE);
return false;
}
- return mGroup.setButtonVisiblity(getId(), false /* visible */) != View.VISIBLE;
+ return mGroup.setButtonVisibility(getId(), false /* visible */) != View.VISIBLE;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButtonGroup.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButtonGroup.java
index 02b660f4734d..9e843f93d00e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButtonGroup.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButtonGroup.java
@@ -37,7 +37,7 @@ public class ContextualButtonGroup extends ButtonDispatcher {
/**
* Add a contextual button to the group. The order of adding increases in its priority. The
* priority is used to determine which button should be visible when setting multiple button's
- * visibility {@see setButtonVisiblity}.
+ * visibility {@see setButtonVisibility}.
* @param button the button added to the group
*/
public void addButton(@NonNull ContextualButton button) {
@@ -71,7 +71,7 @@ public class ContextualButtonGroup extends ButtonDispatcher {
* @return if the button is visible after operation
* @throws RuntimeException if the input id does not match any of the ids in the group
*/
- public int setButtonVisiblity(@IdRes int buttonResId, boolean visible) {
+ public int setButtonVisibility(@IdRes int buttonResId, boolean visible) {
final int index = getContextButtonIndex(buttonResId);
if (index == INVALID_INDEX) {
throw new RuntimeException("Cannot find the button id of " + buttonResId
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
index 404c07b807e5..963fc54ecd2d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
@@ -71,6 +71,7 @@ public class NavigationBarInflaterView extends FrameLayout
public static final String RIGHT = "right";
public static final String CONTEXTUAL = "contextual";
public static final String IME_SWITCHER = "ime_switcher";
+ public static final String START_CONTEXTUAL = "start_contextual";
public static final String GRAVITY_SEPARATOR = ";";
public static final String BUTTON_SEPARATOR = ",";
@@ -419,6 +420,8 @@ public class NavigationBarInflaterView extends FrameLayout
v = inflater.inflate(R.layout.home_handle, parent, false);
} else if (IME_SWITCHER.equals(button)) {
v = inflater.inflate(R.layout.ime_switcher, parent, false);
+ } else if (START_CONTEXTUAL.equals(button)) {
+ v = inflater.inflate(R.layout.start_contextual, parent, false);
} else if (button.startsWith(KEY)) {
String uri = extractImage(button);
int code = extractKeycode(button);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 6f1e161cf237..4333200e702a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -49,6 +49,8 @@ import android.view.MotionEvent;
import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewTreeObserver.InternalInsetsInfo;
+import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
@@ -135,6 +137,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
private boolean mImeVisible;
private final SparseArray<ButtonDispatcher> mButtonDispatchers = new SparseArray<>();
+ private final ContextualButtonGroup mStartContextualButtonGroup;
private final ContextualButtonGroup mContextualButtonGroup;
private Configuration mConfiguration;
private Configuration mTmpLastConfiguration;
@@ -233,11 +236,36 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
}
};
+ private final OnComputeInternalInsetsListener mOnComputeInternalInsetsListener = info -> {
+ // When the nav bar is in 2-button or 3-button mode, or when IME is visible in fully
+ // gestural mode, the entire nav bar should be touchable.
+ if (!QuickStepContract.isGesturalMode(mNavBarMode) || mImeVisible) {
+ info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
+ return;
+ }
+ info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+ RotationContextButton rotationContextButton = getRotateSuggestionButton();
+ // If the rotate suggestion button is not visible in fully gestural mode, the entire nav bar
+ // is not touchable so that the app underneath can be clicked through.
+ if (rotationContextButton.getVisibility() != VISIBLE) {
+ info.touchableRegion.setEmpty();
+ } else {
+ // Set the rotate suggestion button area to be touchable.
+ rotationContextButton.getCurrentView().getLocationInWindow(mTmpPosition);
+ Rect rect = new Rect(mTmpPosition[0], mTmpPosition[1],
+ mTmpPosition[0] + mRotationButtonBounds.width(),
+ mTmpPosition[1] + mRotationButtonBounds.height());
+ info.touchableRegion.union(rect);
+ }
+ };
+
public NavigationBarView(Context context, AttributeSet attrs) {
super(context, attrs);
mIsVertical = false;
mLongClickableAccessibilityButton = false;
+ mNavBarMode = Dependency.get(NavigationModeController.class).addListener(this);
+ boolean isGesturalMode = QuickStepContract.isGesturalMode(mNavBarMode);
// Set up the context group of buttons
mContextualButtonGroup = new ContextualButtonGroup(R.id.menu_container);
@@ -253,12 +281,21 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
R.drawable.ic_sysbar_accessibility_button);
mContextualButtonGroup.addButton(menuButton);
mContextualButtonGroup.addButton(imeSwitcherButton);
- mContextualButtonGroup.addButton(rotateSuggestionButton);
+ if (!isGesturalMode) {
+ mContextualButtonGroup.addButton(rotateSuggestionButton);
+ }
mContextualButtonGroup.addButton(accessibilityButton);
mOverviewProxyService = Dependency.get(OverviewProxyService.class);
mRecentsOnboarding = new RecentsOnboarding(context, mOverviewProxyService);
+ final ContextualButton backButton = new ContextualButton(R.id.back, 0);
+ mStartContextualButtonGroup = new ContextualButtonGroup(R.id.start_menu_container);
+ if (isGesturalMode) {
+ mStartContextualButtonGroup.addButton(rotateSuggestionButton);
+ }
+ mStartContextualButtonGroup.addButton(backButton);
+
mConfiguration = new Configuration();
mTmpLastConfiguration = new Configuration();
mConfiguration.updateFrom(context.getResources().getConfiguration());
@@ -266,7 +303,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
mScreenPinningNotify = new ScreenPinningNotify(mContext);
mBarTransitions = new NavigationBarTransitions(this);
- mButtonDispatchers.put(R.id.back, new ButtonDispatcher(R.id.back));
+ mButtonDispatchers.put(R.id.back, backButton);
mButtonDispatchers.put(R.id.home, new ButtonDispatcher(R.id.home));
mButtonDispatchers.put(R.id.home_handle, new ButtonDispatcher(R.id.home_handle));
mButtonDispatchers.put(R.id.recent_apps, new ButtonDispatcher(R.id.recent_apps));
@@ -275,6 +312,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
mButtonDispatchers.put(R.id.accessibility_button, accessibilityButton);
mButtonDispatchers.put(R.id.rotate_suggestion, rotateSuggestionButton);
mButtonDispatchers.put(R.id.menu_container, mContextualButtonGroup);
+ mButtonDispatchers.put(R.id.start_menu_container, mStartContextualButtonGroup);
mDeadZone = new DeadZone(this);
mEdgeBackGestureHandler = new EdgeBackGestureHandler(context, mOverviewProxyService);
@@ -390,8 +428,11 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
}
public RotationContextButton getRotateSuggestionButton() {
- return (RotationContextButton) mContextualButtonGroup
- .getContextButton(R.id.rotate_suggestion);
+ return (RotationContextButton) mButtonDispatchers.get(R.id.rotate_suggestion);
+ }
+
+ public ContextualButtonGroup getStartContextualButtonGroup() {
+ return mStartContextualButtonGroup;
}
public ButtonDispatcher getHomeHandle() {
@@ -430,6 +471,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
if (densityChange || dirChange) {
mRecentIcon = getDrawable(R.drawable.ic_sysbar_recent);
mContextualButtonGroup.updateIcons();
+ mStartContextualButtonGroup.updateIcons();
}
if (orientationChange || densityChange || dirChange) {
mBackIcon = getBackDrawable();
@@ -437,12 +479,16 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
}
public KeyButtonDrawable getBackDrawable() {
- KeyButtonDrawable drawable = chooseNavigationIconDrawable(R.drawable.ic_sysbar_back,
- R.drawable.ic_sysbar_back_quick_step);
+ KeyButtonDrawable drawable = getDrawable(getBackDrawableRes());
orientBackButton(drawable);
return drawable;
}
+ public @DrawableRes int getBackDrawableRes() {
+ return chooseNavigationIconDrawableRes(R.drawable.ic_sysbar_back,
+ R.drawable.ic_sysbar_back_quick_step);
+ }
+
public KeyButtonDrawable getHomeDrawable() {
final boolean quickStepEnabled = mOverviewProxyService.shouldShowSwipeUpUI();
KeyButtonDrawable drawable = quickStepEnabled
@@ -485,8 +531,13 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
private KeyButtonDrawable chooseNavigationIconDrawable(@DrawableRes int icon,
@DrawableRes int quickStepIcon) {
+ return getDrawable(chooseNavigationIconDrawableRes(icon, quickStepIcon));
+ }
+
+ private @DrawableRes int chooseNavigationIconDrawableRes(@DrawableRes int icon,
+ @DrawableRes int quickStepIcon) {
final boolean quickStepEnabled = mOverviewProxyService.shouldShowSwipeUpUI();
- return quickStepEnabled ? getDrawable(quickStepIcon) : getDrawable(icon);
+ return quickStepEnabled ? quickStepIcon : icon;
}
private KeyButtonDrawable getDrawable(@DrawableRes int icon) {
@@ -527,7 +578,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
mTransitionListener.onBackAltCleared();
}
mImeVisible = visible;
- updateWindowTouchable();
}
public void setDisabledFlags(int disabledFlags) {
@@ -564,7 +614,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
updateRecentsIcon();
// Update IME button visibility, a11y and rotate button always overrides the appearance
- mContextualButtonGroup.setButtonVisiblity(R.id.ime_switcher,
+ mContextualButtonGroup.setButtonVisibility(R.id.ime_switcher,
(mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0);
mBarTransitions.reapplyDarkIntensity();
@@ -609,6 +659,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
}
getBackButton().setVisibility(disableBack ? View.INVISIBLE : View.VISIBLE);
+ mStartContextualButtonGroup.setButtonVisibility(R.id.back, !disableBack);
getHomeButton().setVisibility(disableHome ? View.INVISIBLE : View.VISIBLE);
getRecentsButton().setVisibility(disableRecent ? View.INVISIBLE : View.VISIBLE);
}
@@ -714,11 +765,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
setWindowFlag(WindowManager.LayoutParams.FLAG_SLIPPERY, slippery);
}
- public void updateWindowTouchable() {
- boolean touchable = mImeVisible || !QuickStepContract.isGesturalMode(mNavBarMode);
- setWindowFlag(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, !touchable);
- }
-
private void setWindowFlag(int flags, boolean enable) {
final ViewGroup navbarView = ((ViewGroup) getParent());
if (navbarView == null) {
@@ -743,6 +789,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
mBarTransitions.onNavigationModeChanged(mNavBarMode);
mEdgeBackGestureHandler.onNavigationModeChanged(mNavBarMode);
mRecentsOnboarding.onNavigationModeChanged(mNavBarMode);
+ getRotateSuggestionButton().onNavigationModeChanged(mNavBarMode);
// Color adaption is tied with showing home handle, only avaliable if visible
mTintController.onNavigationModeChanged(mNavBarMode);
@@ -751,17 +798,16 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
} else {
mTintController.stop();
}
- updateWindowTouchable();
}
public void setMenuVisibility(final boolean show) {
- mContextualButtonGroup.setButtonVisiblity(R.id.menu, show);
+ mContextualButtonGroup.setButtonVisibility(R.id.menu, show);
}
public void setAccessibilityButtonState(final boolean visible, final boolean longClickable) {
mLongClickableAccessibilityButton = longClickable;
getAccessibilityButton().setLongClickable(longClickable);
- mContextualButtonGroup.setButtonVisiblity(R.id.accessibility_button, visible);
+ mContextualButtonGroup.setButtonVisibility(R.id.accessibility_button, visible);
}
void hideRecentsOnboarding() {
@@ -1044,12 +1090,11 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
onPluginDisconnected(null); // Create default gesture helper
Dependency.get(PluginManager.class).addPluginListener(this,
NavGesture.class, false /* Only one */);
- int navBarMode = Dependency.get(NavigationModeController.class).addListener(this);
- onNavigationModeChanged(navBarMode);
+ onNavigationModeChanged(mNavBarMode);
setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled());
mEdgeBackGestureHandler.onNavBarAttached();
- updateWindowTouchable();
+ getViewTreeObserver().addOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener);
}
@Override
@@ -1065,6 +1110,8 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
mButtonDispatchers.valueAt(i).onDestroy();
}
mEdgeBackGestureHandler.onNavBarDetached();
+ getViewTreeObserver().removeOnComputeInternalInsetsListener(
+ mOnComputeInternalInsetsListener);
}
private void setUpSwipeUpOnboarding(boolean connectedToOverviewProxy) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java
index e887f5b573cd..7203e57c3d23 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.phone;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
+
import static com.android.internal.view.RotationPolicy.NATURAL_ROTATION;
import android.animation.Animator;
@@ -45,6 +47,7 @@ import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.statusbar.policy.KeyButtonDrawable;
import com.android.systemui.statusbar.policy.RotationLockController;
@@ -52,7 +55,9 @@ import com.android.systemui.statusbar.policy.RotationLockController;
import java.util.Optional;
import java.util.function.Consumer;
-public class RotationContextButton extends ContextualButton {
+/** Containing logic for the rotation button in nav bar. */
+public class RotationContextButton extends ContextualButton implements
+ NavigationModeController.ModeChangedListener {
public static final boolean DEBUG_ROTATION = false;
private static final int BUTTON_FADE_IN_OUT_DURATION_MS = 100;
@@ -76,6 +81,7 @@ public class RotationContextButton extends ContextualButton {
() -> mPendingRotationSuggestion = false;
private Animator mRotateHideAnimator;
private boolean mAccessibilityFeedbackEnabled;
+ private int mNavBarMode = NAV_BAR_MODE_3BUTTON;
private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
private final ViewRippler mViewRippler = new ViewRippler();
@@ -304,7 +310,8 @@ public class RotationContextButton extends ContextualButton {
@Override
protected KeyButtonDrawable getNewDrawable() {
Context context = new ContextThemeWrapper(getContext().getApplicationContext(), mStyleRes);
- return KeyButtonDrawable.create(context, mIconResId, false /* shadow */);
+ return KeyButtonDrawable.create(context, mIconResId, false /* shadow */,
+ QuickStepContract.isGesturalMode(mNavBarMode));
}
@Override
@@ -390,9 +397,9 @@ public class RotationContextButton extends ContextualButton {
}
private int computeRotationProposalTimeout() {
- if (mAccessibilityFeedbackEnabled) return 20000;
- if (mHoveringRotationSuggestion) return 16000;
- return 10000;
+ if (mAccessibilityFeedbackEnabled) return 10000;
+ if (mHoveringRotationSuggestion) return 8000;
+ return 5000;
}
private boolean isRotateSuggestionIntroduced() {
@@ -414,6 +421,11 @@ public class RotationContextButton extends ContextualButton {
}
}
+ @Override
+ public void onNavigationModeChanged(int mode) {
+ mNavBarMode = mode;
+ }
+
private class TaskStackListenerImpl extends TaskStackChangeListener {
// Invalidate any rotation suggestion on task change or activity orientation change
// Note: all callbacks happen on main thread
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java
index dd0c3443f2ab..2bfc311f1bd4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java
@@ -30,9 +30,11 @@ import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
import android.util.FloatProperty;
@@ -77,13 +79,14 @@ public class KeyButtonDrawable extends Drawable {
private final Paint mIconPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
private final Paint mShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
+ private final Paint mOvalBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
private final ShadowDrawableState mState;
private AnimatedVectorDrawable mAnimatedDrawable;
public KeyButtonDrawable(Drawable d, @ColorInt int lightColor, @ColorInt int darkColor,
- boolean horizontalFlip) {
+ boolean horizontalFlip, boolean hasOvalBg) {
this(d, new ShadowDrawableState(lightColor, darkColor,
- d instanceof AnimatedVectorDrawable, horizontalFlip));
+ d instanceof AnimatedVectorDrawable, horizontalFlip, hasOvalBg));
}
private KeyButtonDrawable(Drawable d, ShadowDrawableState state) {
@@ -98,6 +101,7 @@ public class KeyButtonDrawable extends Drawable {
mAnimatedDrawable = (AnimatedVectorDrawable) mState.mChildState.newDrawable().mutate();
setDrawableBounds(mAnimatedDrawable);
}
+ mOvalBgPaint.setColor(mState.mDarkColor);
}
public void setDarkIntensity(float intensity) {
@@ -165,7 +169,12 @@ public class KeyButtonDrawable extends Drawable {
public void setColorFilter(ColorFilter colorFilter) {
mIconPaint.setColorFilter(colorFilter);
if (mAnimatedDrawable != null) {
- mAnimatedDrawable.setColorFilter(colorFilter);
+ if (mState.mHasOvalBg) {
+ mAnimatedDrawable.setColorFilter(
+ new PorterDuffColorFilter(mState.mLightColor, PorterDuff.Mode.SRC_IN));
+ } else {
+ mAnimatedDrawable.setColorFilter(colorFilter);
+ }
}
invalidateSelf();
}
@@ -235,6 +244,10 @@ public class KeyButtonDrawable extends Drawable {
return;
}
+ if (mState.mHasOvalBg) {
+ canvas.drawOval(new RectF(bounds), mOvalBgPaint);
+ }
+
if (mAnimatedDrawable != null) {
mAnimatedDrawable.draw(canvas);
} else {
@@ -379,14 +392,16 @@ public class KeyButtonDrawable extends Drawable {
final int mLightColor;
final int mDarkColor;
final boolean mSupportsAnimation;
+ final boolean mHasOvalBg;
public ShadowDrawableState(@ColorInt int lightColor, @ColorInt int darkColor,
- boolean animated, boolean horizontalFlip) {
+ boolean animated, boolean horizontalFlip, boolean hasOvalBg) {
mLightColor = lightColor;
mDarkColor = darkColor;
mSupportsAnimation = animated;
mAlpha = 255;
mHorizontalFlip = horizontalFlip;
+ mHasOvalBg = hasOvalBg;
}
@Override
@@ -411,32 +426,51 @@ public class KeyButtonDrawable extends Drawable {
* @param ctx Context to get the drawable and determine the dark and light theme
* @param icon the icon resource id
* @param hasShadow if a shadow will appear with the drawable
+ * @param hasOvalBg if an oval bg will be drawn
* @return KeyButtonDrawable
*/
public static KeyButtonDrawable create(@NonNull Context ctx, @DrawableRes int icon,
- boolean hasShadow) {
+ boolean hasShadow, boolean hasOvalBg) {
final int dualToneDarkTheme = Utils.getThemeAttr(ctx, R.attr.darkIconTheme);
final int dualToneLightTheme = Utils.getThemeAttr(ctx, R.attr.lightIconTheme);
Context lightContext = new ContextThemeWrapper(ctx, dualToneLightTheme);
Context darkContext = new ContextThemeWrapper(ctx, dualToneDarkTheme);
- return KeyButtonDrawable.create(lightContext, darkContext, icon, hasShadow);
+ return KeyButtonDrawable.create(lightContext, darkContext, icon, hasShadow, hasOvalBg);
+ }
+
+ /**
+ * Creates a KeyButtonDrawable with a shadow given its icon. For more information, see
+ * {@link #create(Context, int, boolean, boolean)}.
+ */
+ public static KeyButtonDrawable create(@NonNull Context ctx, @DrawableRes int icon,
+ boolean hasShadow) {
+ return create(ctx, icon, hasShadow, false /* hasOvalBg */);
}
+ /**
+ * Creates a KeyButtonDrawable with a shadow given its icon. For more information, see
+ * {@link #create(Context, int, boolean, boolean)}.
+ */
public static KeyButtonDrawable create(Context lightContext, Context darkContext,
- @DrawableRes int iconResId, boolean hasShadow) {
+ @DrawableRes int iconResId, boolean hasShadow, boolean hasOvalBg) {
return create(lightContext,
Utils.getColorAttrDefaultColor(lightContext, R.attr.singleToneColor),
Utils.getColorAttrDefaultColor(darkContext, R.attr.singleToneColor),
- iconResId, hasShadow);
+ iconResId, hasShadow, hasOvalBg);
}
+ /**
+ * Creates a KeyButtonDrawable with a shadow given its icon. For more information, see
+ * {@link #create(Context, int, boolean, boolean)}.
+ */
public static KeyButtonDrawable create(Context context, @ColorInt int lightColor,
- @ColorInt int darkColor, @DrawableRes int iconResId, boolean hasShadow) {
+ @ColorInt int darkColor, @DrawableRes int iconResId, boolean hasShadow,
+ boolean hasOvalBg) {
final Resources res = context.getResources();
boolean isRtl = res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
Drawable d = context.getDrawable(iconResId);
final KeyButtonDrawable drawable = new KeyButtonDrawable(d, lightColor, darkColor,
- isRtl && d.isAutoMirrored());
+ isRtl && d.isAutoMirrored(), hasOvalBg);
if (hasShadow) {
int offsetX = res.getDimensionPixelSize(R.dimen.nav_key_button_shadow_offset_x);
int offsetY = res.getDimensionPixelSize(R.dimen.nav_key_button_shadow_offset_y);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarContextTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarContextTest.java
index c837c9ccea95..cb70a1fa3a3b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarContextTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarContextTest.java
@@ -80,7 +80,7 @@ public class NavigationBarContextTest extends SysuiTestCase {
@Test
public void testSetButtonVisibility() throws Exception {
- assertFalse("By default the group should be invisible.", mGroup.isVisible());
+ assertTrue("By default the group should be visible.", mGroup.isVisible());
// Set button 1 to be visible, make sure it is the only visible button
showButton(mBtn1);
@@ -89,7 +89,7 @@ public class NavigationBarContextTest extends SysuiTestCase {
assertFalse(mBtn2.isVisible());
// Hide button 1 and make sure the group is also invisible
- assertNotEquals(mGroup.setButtonVisiblity(BUTTON_1_ID, false /* visible */), View.VISIBLE);
+ assertNotEquals(mGroup.setButtonVisibility(BUTTON_1_ID, false /* visible */), View.VISIBLE);
assertFalse("No buttons are visible, group should also be hidden", mGroup.isVisible());
assertNull("No buttons should be visible", mGroup.getVisibleContextButton());
}
@@ -97,7 +97,7 @@ public class NavigationBarContextTest extends SysuiTestCase {
@Test(expected = RuntimeException.class)
public void testSetButtonVisibilityUnaddedButton() throws Exception {
int id = mBtn2.getId() + 1;
- mGroup.setButtonVisiblity(id, true /* visible */);
+ mGroup.setButtonVisibility(id, true /* visible */);
fail("Did not throw when setting a button with an invalid id");
}
@@ -120,17 +120,17 @@ public class NavigationBarContextTest extends SysuiTestCase {
assertTrue(mGroup.isButtonVisibleWithinGroup(mBtn2.getId()));
// Hide button 2
- assertNotEquals(mGroup.setButtonVisiblity(BUTTON_2_ID, false /* visible */), View.VISIBLE);
+ assertNotEquals(mGroup.setButtonVisibility(BUTTON_2_ID, false /* visible */), View.VISIBLE);
assertEquals("Hiding button 2 should show button 1", mBtn1,
mGroup.getVisibleContextButton());
// Hide button 1
- assertNotEquals(mGroup.setButtonVisiblity(BUTTON_1_ID, false /* visible */), View.VISIBLE);
+ assertNotEquals(mGroup.setButtonVisibility(BUTTON_1_ID, false /* visible */), View.VISIBLE);
assertEquals("Hiding button 1 should show button 0", mBtn0,
mGroup.getVisibleContextButton());
// Hide button 0, all buttons are now invisible
- assertNotEquals(mGroup.setButtonVisiblity(BUTTON_0_ID, false /* visible */), View.VISIBLE);
+ assertNotEquals(mGroup.setButtonVisibility(BUTTON_0_ID, false /* visible */), View.VISIBLE);
assertFalse("No buttons are visible, group should also be invisible", mGroup.isVisible());
assertNull(mGroup.getVisibleContextButton());
assertFalse(mGroup.isButtonVisibleWithinGroup(mBtn0.getId()));
@@ -144,7 +144,7 @@ public class NavigationBarContextTest extends SysuiTestCase {
showButton(mBtn2);
// Show button 1
- assertNotEquals(mGroup.setButtonVisiblity(BUTTON_1_ID, true /* visible */), View.VISIBLE);
+ assertNotEquals(mGroup.setButtonVisibility(BUTTON_1_ID, true /* visible */), View.VISIBLE);
assertTrue("Showing button 1 lower priority should be hidden but visible underneath",
mGroup.isButtonVisibleWithinGroup(BUTTON_1_ID));
assertFalse(mBtn0.isVisible());
@@ -152,7 +152,7 @@ public class NavigationBarContextTest extends SysuiTestCase {
assertTrue(mBtn2.isVisible());
// Hide button 1
- assertNotEquals(mGroup.setButtonVisiblity(BUTTON_1_ID, false /* visible */), View.VISIBLE);
+ assertNotEquals(mGroup.setButtonVisibility(BUTTON_1_ID, false /* visible */), View.VISIBLE);
assertFalse("Hiding button 1 with lower priority hides itself underneath",
mGroup.isButtonVisibleWithinGroup(BUTTON_1_ID));
assertTrue("A button still visible, group should also be visible", mGroup.isVisible());
@@ -180,9 +180,9 @@ public class NavigationBarContextTest extends SysuiTestCase {
final Drawable d = mock(Drawable.class);
final ContextualButton button = spy(mBtn0);
final KeyButtonDrawable kbd1 = spy(new KeyButtonDrawable(d, unusedColor, unusedColor,
- false /* horizontalFlip */));
+ false /* horizontalFlip */, false /* hasOvalBg */));
final KeyButtonDrawable kbd2 = spy(new KeyButtonDrawable(d, unusedColor, unusedColor,
- false /* horizontalFlip */));
+ false /* horizontalFlip */, false /* hasOvalBg */));
kbd1.setDarkIntensity(TEST_DARK_INTENSITY);
kbd2.setDarkIntensity(0f);
@@ -198,7 +198,7 @@ public class NavigationBarContextTest extends SysuiTestCase {
}
private void showButton(ContextualButton button) {
- assertEquals(View.VISIBLE, mGroup.setButtonVisiblity(button.getId(), true /* visible */));
+ assertEquals(View.VISIBLE, mGroup.setButtonVisibility(button.getId(), true /* visible */));
assertTrue("After set a button visible, group should also be visible", mGroup.isVisible());
assertEquals(button, mGroup.getVisibleContextButton());
}
diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java
index b50af28b12b3..032af258dbd9 100644
--- a/services/core/java/com/android/server/attention/AttentionManagerService.java
+++ b/services/core/java/com/android/server/attention/AttentionManagerService.java
@@ -22,7 +22,6 @@ import static android.service.attention.AttentionService.ATTENTION_FAILURE_UNKNO
import android.Manifest;
import android.annotation.Nullable;
-import android.annotation.TestApi;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.attention.AttentionManagerInternal;
@@ -43,6 +42,9 @@ import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.os.ShellCommand;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.DeviceConfig;
@@ -56,7 +58,6 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.StatsLog;
-import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
@@ -76,15 +77,6 @@ public class AttentionManagerService extends SystemService {
private static final String LOG_TAG = "AttentionManagerService";
private static final boolean DEBUG = false;
- /**
- * DeviceConfig flag name, allows a CTS to inject a fake implementation.
- *
- * @hide
- */
- @TestApi
- public static final String COMPONENT_NAME = "component_name";
-
-
/** Default value in absence of {@link DeviceConfig} override. */
private static final boolean DEFAULT_SERVICE_ENABLED = true;
@@ -96,6 +88,7 @@ public class AttentionManagerService extends SystemService {
/** DeviceConfig flag name, if {@code true}, enables AttentionManagerService features. */
private static final String SERVICE_ENABLED = "service_enabled";
+ private static String sTestAttentionServicePackage;
private final Context mContext;
private final PowerManager mPowerManager;
private final Object mLock;
@@ -135,7 +128,7 @@ public class AttentionManagerService extends SystemService {
/** Returns {@code true} if attention service is configured on this device. */
public static boolean isServiceConfigured(Context context) {
- return !TextUtils.isEmpty(getServiceConfig(context));
+ return !TextUtils.isEmpty(getServiceConfigPackage(context));
}
/** Resolves and sets up the attention service if it had not been done yet. */
@@ -332,8 +325,8 @@ public class AttentionManagerService extends SystemService {
return mUserStates.get(userId);
}
- private static String getServiceConfig(Context context) {
- return context.getString(R.string.config_defaultAttentionService);
+ private static String getServiceConfigPackage(Context context) {
+ return context.getPackageManager().getAttentionServicePackageName();
}
/**
@@ -341,28 +334,26 @@ public class AttentionManagerService extends SystemService {
* system.
*/
private static ComponentName resolveAttentionService(Context context) {
- final String flag = DeviceConfig.getProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
- COMPONENT_NAME);
-
- final String componentNameString = flag != null ? flag : getServiceConfig(context);
- if (TextUtils.isEmpty(componentNameString)) {
- return null;
- }
-
- final ComponentName componentName = ComponentName.unflattenFromString(componentNameString);
- if (componentName == null) {
+ final String serviceConfigPackage = getServiceConfigPackage(context);
+
+ String resolvedPackage;
+ int flags = PackageManager.MATCH_SYSTEM_ONLY;
+ if (!TextUtils.isEmpty(sTestAttentionServicePackage)) {
+ resolvedPackage = sTestAttentionServicePackage;
+ flags = PackageManager.GET_META_DATA;
+ } else if (!TextUtils.isEmpty(serviceConfigPackage)) {
+ resolvedPackage = serviceConfigPackage;
+ } else {
return null;
}
final Intent intent = new Intent(AttentionService.SERVICE_INTERFACE).setPackage(
- componentName.getPackageName());
+ resolvedPackage);
- // Make sure that only system apps can declare the AttentionService.
- final ResolveInfo resolveInfo = context.getPackageManager().resolveService(intent,
- PackageManager.MATCH_SYSTEM_ONLY);
+ final ResolveInfo resolveInfo = context.getPackageManager().resolveService(intent, flags);
if (resolveInfo == null || resolveInfo.serviceInfo == null) {
Slog.wtf(LOG_TAG, String.format("Service %s not found in package %s",
- AttentionService.SERVICE_INTERFACE, componentName
+ AttentionService.SERVICE_INTERFACE, serviceConfigPackage
));
return null;
}
@@ -447,6 +438,7 @@ public class AttentionManagerService extends SystemService {
}
void cancelInternal() {
+ mIsFulfilled = true;
mCallbackInternal.onFailure(ATTENTION_FAILURE_CANCELLED);
}
}
@@ -606,7 +598,6 @@ public class AttentionManagerService extends SystemService {
}
return;
}
- userState.mCurrentAttentionCheck.mIsFulfilled = true;
if (userState.mService == null) {
userState.mCurrentAttentionCheck.cancelInternal();
@@ -654,7 +645,165 @@ public class AttentionManagerService extends SystemService {
}
}
+ private final class AttentionManagerServiceShellCommand extends ShellCommand {
+ class TestableAttentionCallbackInternal extends AttentionCallbackInternal {
+ private int mLastCallbackCode = -1;
+
+ @Override
+ public void onSuccess(int result, long timestamp) {
+ mLastCallbackCode = result;
+ }
+
+ @Override
+ public void onFailure(int error) {
+ mLastCallbackCode = error;
+ }
+
+ public void reset() {
+ mLastCallbackCode = -1;
+ }
+
+ public int getLastCallbackCode() {
+ return mLastCallbackCode;
+ }
+ }
+
+ final TestableAttentionCallbackInternal mTestableAttentionCallback =
+ new TestableAttentionCallbackInternal();
+
+ @Override
+ public int onCommand(@Nullable final String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+ final PrintWriter err = getErrPrintWriter();
+ try {
+ switch (cmd) {
+ case "getAttentionServiceComponent":
+ return cmdResolveAttentionServiceComponent();
+ case "call":
+ switch (getNextArgRequired()) {
+ case "checkAttention":
+ return cmdCallCheckAttention();
+ case "cancelCheckAttention":
+ return cmdCallCancelAttention();
+ default:
+ throw new IllegalArgumentException("Invalid argument");
+ }
+ case "setTestableAttentionService":
+ return cmdSetTestableAttentionService(getNextArgRequired());
+ case "clearTestableAttentionService":
+ return cmdClearTestableAttentionService();
+ case "getLastTestCallbackCode":
+ return cmdGetLastTestCallbackCode();
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ } catch (IllegalArgumentException e) {
+ err.println("Error: " + e.getMessage());
+ }
+ return -1;
+ }
+
+ private int cmdSetTestableAttentionService(String testingServicePackage) {
+ final PrintWriter out = getOutPrintWriter();
+ if (TextUtils.isEmpty(testingServicePackage)) {
+ out.println("false");
+ } else {
+ sTestAttentionServicePackage = testingServicePackage;
+ resetStates();
+ out.println(mComponentName != null ? "true" : "false");
+ }
+ return 0;
+ }
+
+ private int cmdClearTestableAttentionService() {
+ sTestAttentionServicePackage = "";
+ mTestableAttentionCallback.reset();
+ resetStates();
+ return 0;
+ }
+
+ private int cmdCallCheckAttention() {
+ final PrintWriter out = getOutPrintWriter();
+ boolean calledSuccessfully = checkAttention(2000, mTestableAttentionCallback);
+ out.println(calledSuccessfully ? "true" : "false");
+ return 0;
+ }
+
+ private int cmdCallCancelAttention() {
+ final PrintWriter out = getOutPrintWriter();
+ cancelAttentionCheck(mTestableAttentionCallback);
+ out.println("true");
+ return 0;
+ }
+
+ private int cmdResolveAttentionServiceComponent() {
+ final PrintWriter out = getOutPrintWriter();
+ ComponentName resolvedComponent = resolveAttentionService(mContext);
+ out.println(resolvedComponent != null ? resolvedComponent.flattenToShortString() : "");
+ return 0;
+ }
+
+ private int cmdGetLastTestCallbackCode() {
+ final PrintWriter out = getOutPrintWriter();
+ out.println(mTestableAttentionCallback.getLastCallbackCode());
+ return 0;
+ }
+
+ private void resetStates() {
+ mComponentName = resolveAttentionService(mContext);
+ mUserStates.clear();
+ }
+
+ @Override
+ public void onHelp() {
+ final PrintWriter out = getOutPrintWriter();
+ out.println("Attention commands: ");
+ out.println(" setTestableAttentionService <service_package>: Bind to a custom"
+ + " implementation of attention service");
+ out.println(" ---<service_package>:");
+ out.println(
+ " := Package containing the Attention Service implementation to bind to");
+ out.println(" ---returns:");
+ out.println(" := true, if was bound successfully");
+ out.println(" := false, if was not bound successfully");
+ out.println(" clearTestableAttentionService: Undo custom bindings. Revert to previous"
+ + " behavior");
+ out.println(" getAttentionServiceComponent: Get the current service component string");
+ out.println(" ---returns:");
+ out.println(" := If valid, the component string (in shorten form) for the"
+ + " currently bound service.");
+ out.println(" := else, empty string");
+ out.println(" call checkAttention: Calls check attention");
+ out.println(" ---returns:");
+ out.println(
+ " := true, if the call was successfully dispatched to the service "
+ + "implementation."
+ + " (to see the result, call getLastTestCallbackCode)");
+ out.println(" := false, otherwise");
+ out.println(" call cancelCheckAttention: Cancels check attention");
+ out.println(" getLastTestCallbackCode");
+ out.println(" ---returns:");
+ out.println(
+ " := An integer, representing the last callback code received from the "
+ + "bounded implementation. If none, it will return -1");
+ }
+ }
+
private final class BinderService extends Binder {
+ AttentionManagerServiceShellCommand mAttentionManagerServiceShellCommand =
+ new AttentionManagerServiceShellCommand();
+
+ @Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out,
+ FileDescriptor err,
+ String[] args, ShellCallback callback,
+ ResultReceiver resultReceiver) {
+ mAttentionManagerServiceShellCommand.exec(this, in, out, err, args, callback,
+ resultReceiver);
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) {
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 615472661f9e..db2c742b8d4e 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -70,6 +70,7 @@ import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -588,6 +589,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
// rollback sessions been applied.
List<RollbackData> enabling = new ArrayList<>();
List<RollbackData> restoreInProgress = new ArrayList<>();
+ Set<String> apexPackageNames = new HashSet<>();
synchronized (mLock) {
ensureRollbackDataLoadedLocked();
for (RollbackData data : mRollbacks) {
@@ -597,6 +599,12 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
} else if (data.restoreUserDataInProgress) {
restoreInProgress.add(data);
}
+
+ for (PackageRollbackInfo info : data.info.getPackages()) {
+ if (info.isApex()) {
+ apexPackageNames.add(info.getPackageName());
+ }
+ }
}
}
}
@@ -634,6 +642,14 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
}
}
+ for (String apexPackageName : apexPackageNames) {
+ // We will not recieve notifications when an apex is updated,
+ // so check now in case any rollbacks ought to be expired. The
+ // onPackagedReplace function is safe to call if the package
+ // hasn't actually been updated.
+ onPackageReplaced(apexPackageName);
+ }
+
mPackageHealthObserver.onBootCompleted();
});
}
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 4ed07c351847..8a834c8f942b 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -5438,7 +5438,7 @@ class ActivityStack extends ConfigurationContainer {
if (isAttached()) {
getDisplay().positionChildAtBottom(this);
}
- if (!isActivityTypeHome() || getDisplay().isRemoved()) {
+ if (!isActivityTypeHome() || !isAttached()) {
remove();
}
}
diff --git a/services/net/java/android/net/ip/IIpClient.aidl b/services/net/java/android/net/ip/IIpClient.aidl
index 1e7726472ff6..9989c52fc403 100644
--- a/services/net/java/android/net/ip/IIpClient.aidl
+++ b/services/net/java/android/net/ip/IIpClient.aidl
@@ -17,6 +17,7 @@ package android.net.ip;
import android.net.ProxyInfo;
import android.net.ProvisioningConfigurationParcelable;
+import android.net.NattKeepalivePacketDataParcelable;
import android.net.TcpKeepalivePacketDataParcelable;
/** @hide */
@@ -33,4 +34,5 @@ oneway interface IIpClient {
void addKeepalivePacketFilter(int slot, in TcpKeepalivePacketDataParcelable pkt);
void removeKeepalivePacketFilter(int slot);
void setL2KeyAndGroupHint(in String l2Key, in String groupHint);
+ void addNattKeepalivePacketFilter(int slot, in NattKeepalivePacketDataParcelable pkt);
}
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerLogger.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerLogger.java
new file mode 100644
index 000000000000..73b4ce767d0d
--- /dev/null
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerLogger.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.soundtrigger;
+
+import android.util.Log;
+
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.LinkedList;
+
+/**
+* Constructor SoundTriggerLogger class
+*/
+public class SoundTriggerLogger {
+
+ // ring buffer of events to log.
+ private final LinkedList<Event> mEvents;
+
+ private final String mTitle;
+
+ // the maximum number of events to keep in log
+ private final int mMemSize;
+
+ /**
+ * Constructor for Event class.
+ */
+ public abstract static class Event {
+ // formatter for timestamps
+ private static final SimpleDateFormat sFormat = new SimpleDateFormat("MM-dd HH:mm:ss:SSS");
+
+ private final long mTimestamp;
+
+ Event() {
+ mTimestamp = System.currentTimeMillis();
+ }
+
+ /**
+ * Convert event to String
+ * @return StringBuilder
+ */
+ public String toString() {
+ return (new StringBuilder(sFormat.format(new Date(mTimestamp))))
+ .append(" ").append(eventToString()).toString();
+ }
+
+ /**
+ * Causes the string message for the event to appear in the logcat.
+ * Here is an example of how to create a new event (a StringEvent), adding it to the logger
+ * (an instance of SoundTriggerLogger) while also making it show in the logcat:
+ * <pre>
+ * myLogger.log(
+ * (new StringEvent("something for logcat and logger")).printLog(MyClass.TAG) );
+ * </pre>
+ * @param tag the tag for the android.util.Log.v
+ * @return the same instance of the event
+ */
+ public Event printLog(String tag) {
+ Log.i(tag, eventToString());
+ return this;
+ }
+
+ /**
+ * Convert event to String.
+ * This method is only called when the logger history is about to the dumped,
+ * so this method is where expensive String conversions should be made, not when the Event
+ * subclass is created.
+ * Timestamp information will be automatically added, do not include it.
+ * @return a string representation of the event that occurred.
+ */
+ public abstract String eventToString();
+ }
+
+ /**
+ * Constructor StringEvent class
+ */
+ public static class StringEvent extends Event {
+ private final String mMsg;
+
+ public StringEvent(String msg) {
+ mMsg = msg;
+ }
+
+ @Override
+ public String eventToString() {
+ return mMsg;
+ }
+ }
+
+ /**
+ * Constructor for logger.
+ * @param size the maximum number of events to keep in log
+ * @param title the string displayed before the recorded log
+ */
+ public SoundTriggerLogger(int size, String title) {
+ mEvents = new LinkedList<Event>();
+ mMemSize = size;
+ mTitle = title;
+ }
+
+ /**
+ * Constructor for logger.
+ * @param evt the maximum number of events to keep in log
+ */
+ public synchronized void log(Event evt) {
+ if (mEvents.size() >= mMemSize) {
+ mEvents.removeFirst();
+ }
+ mEvents.add(evt);
+ }
+
+ /**
+ * Constructor for logger.
+ * @param pw the maximum number of events to keep in log
+ */
+ public synchronized void dump(PrintWriter pw) {
+ pw.println("ST Event log: " + mTitle);
+ for (Event evt : mEvents) {
+ pw.println(evt.toString());
+ }
+ }
+}
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index 697469a3c680..9c4c0998b21a 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -180,9 +180,16 @@ public class SoundTriggerService extends SystemService {
Slog.i(TAG, "startRecognition(): Uuid : " + parcelUuid);
}
+ sEventLogger.log(new SoundTriggerLogger.StringEvent("startRecognition(): Uuid : "
+ + parcelUuid));
+
GenericSoundModel model = getSoundModel(parcelUuid);
if (model == null) {
Slog.e(TAG, "Null model in database for id: " + parcelUuid);
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ "startRecognition(): Null model in database for id: " + parcelUuid));
+
return STATUS_ERROR;
}
@@ -196,6 +203,10 @@ public class SoundTriggerService extends SystemService {
if (DEBUG) {
Slog.i(TAG, "stopRecognition(): Uuid : " + parcelUuid);
}
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent("stopRecognition(): Uuid : "
+ + parcelUuid));
+
if (!isInitialized()) return STATUS_ERROR;
return mSoundTriggerHelper.stopGenericRecognition(parcelUuid.getUuid(), callback);
}
@@ -206,6 +217,10 @@ public class SoundTriggerService extends SystemService {
if (DEBUG) {
Slog.i(TAG, "getSoundModel(): id = " + soundModelId);
}
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent("getSoundModel(): id = "
+ + soundModelId));
+
SoundTrigger.GenericSoundModel model = mDbHelper.getGenericSoundModel(
soundModelId.getUuid());
return model;
@@ -217,6 +232,10 @@ public class SoundTriggerService extends SystemService {
if (DEBUG) {
Slog.i(TAG, "updateSoundModel(): model = " + soundModel);
}
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent("updateSoundModel(): model = "
+ + soundModel));
+
mDbHelper.updateGenericSoundModel(soundModel);
}
@@ -226,6 +245,10 @@ public class SoundTriggerService extends SystemService {
if (DEBUG) {
Slog.i(TAG, "deleteSoundModel(): id = " + soundModelId);
}
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent("deleteSoundModel(): id = "
+ + soundModelId));
+
// Unload the model if it is loaded.
mSoundTriggerHelper.unloadGenericSoundModel(soundModelId.getUuid());
mDbHelper.deleteGenericSoundModel(soundModelId.getUuid());
@@ -237,11 +260,19 @@ public class SoundTriggerService extends SystemService {
if (!isInitialized()) return STATUS_ERROR;
if (soundModel == null || soundModel.uuid == null) {
Slog.e(TAG, "Invalid sound model");
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ "loadGenericSoundModel(): Invalid sound model"));
+
return STATUS_ERROR;
}
if (DEBUG) {
Slog.i(TAG, "loadGenericSoundModel(): id = " + soundModel.uuid);
}
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent("loadGenericSoundModel(): id = "
+ + soundModel.uuid));
+
synchronized (mLock) {
SoundModel oldModel = mLoadedModels.get(soundModel.uuid);
// If the model we're loading is actually different than what we had loaded, we
@@ -264,15 +295,28 @@ public class SoundTriggerService extends SystemService {
if (!isInitialized()) return STATUS_ERROR;
if (soundModel == null || soundModel.uuid == null) {
Slog.e(TAG, "Invalid sound model");
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ "loadKeyphraseSoundModel(): Invalid sound model"));
+
return STATUS_ERROR;
}
if (soundModel.keyphrases == null || soundModel.keyphrases.length != 1) {
Slog.e(TAG, "Only one keyphrase per model is currently supported.");
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ "loadKeyphraseSoundModel(): Only one keyphrase per model"
+ + " is currently supported."));
+
return STATUS_ERROR;
}
if (DEBUG) {
Slog.i(TAG, "loadKeyphraseSoundModel(): id = " + soundModel.uuid);
}
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent("loadKeyphraseSoundModel(): id = "
+ + soundModel.uuid));
+
synchronized (mLock) {
SoundModel oldModel = mLoadedModels.get(soundModel.uuid);
// If the model we're loading is actually different than what we had loaded, we
@@ -303,6 +347,9 @@ public class SoundTriggerService extends SystemService {
Slog.i(TAG, "startRecognition(): id = " + soundModelId);
}
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ "startRecognitionForService(): id = " + soundModelId));
+
IRecognitionStatusCallback callback =
new RemoteSoundTriggerDetectionService(soundModelId.getUuid(), params,
detectionService, Binder.getCallingUserHandle(), config);
@@ -311,6 +358,10 @@ public class SoundTriggerService extends SystemService {
SoundModel soundModel = mLoadedModels.get(soundModelId.getUuid());
if (soundModel == null) {
Slog.e(TAG, soundModelId + " is not loaded");
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ "startRecognitionForService():" + soundModelId + " is not loaded"));
+
return STATUS_ERROR;
}
IRecognitionStatusCallback existingCallback = null;
@@ -319,6 +370,11 @@ public class SoundTriggerService extends SystemService {
}
if (existingCallback != null) {
Slog.e(TAG, soundModelId + " is already running");
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ "startRecognitionForService():"
+ + soundModelId + " is already running"));
+
return STATUS_ERROR;
}
int ret;
@@ -329,11 +385,19 @@ public class SoundTriggerService extends SystemService {
break;
default:
Slog.e(TAG, "Unknown model type");
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ "startRecognitionForService(): Unknown model type"));
+
return STATUS_ERROR;
}
if (ret != STATUS_OK) {
Slog.e(TAG, "Failed to start model: " + ret);
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ "startRecognitionForService(): Failed to start model:"));
+
return ret;
}
synchronized (mCallbacksLock) {
@@ -351,10 +415,18 @@ public class SoundTriggerService extends SystemService {
Slog.i(TAG, "stopRecognition(): id = " + soundModelId);
}
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ "stopRecognitionForService(): id = " + soundModelId));
+
synchronized (mLock) {
SoundModel soundModel = mLoadedModels.get(soundModelId.getUuid());
if (soundModel == null) {
Slog.e(TAG, soundModelId + " is not loaded");
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ "stopRecognitionForService(): " + soundModelId
+ + " is not loaded"));
+
return STATUS_ERROR;
}
IRecognitionStatusCallback callback = null;
@@ -363,6 +435,11 @@ public class SoundTriggerService extends SystemService {
}
if (callback == null) {
Slog.e(TAG, soundModelId + " is not running");
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ "stopRecognitionForService(): " + soundModelId
+ + " is not running"));
+
return STATUS_ERROR;
}
int ret;
@@ -372,11 +449,19 @@ public class SoundTriggerService extends SystemService {
break;
default:
Slog.e(TAG, "Unknown model type");
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ "stopRecognitionForService(): Unknown model type"));
+
return STATUS_ERROR;
}
if (ret != STATUS_OK) {
Slog.e(TAG, "Failed to stop model: " + ret);
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ "stopRecognitionForService(): Failed to stop model: " + ret));
+
return ret;
}
synchronized (mCallbacksLock) {
@@ -394,10 +479,17 @@ public class SoundTriggerService extends SystemService {
Slog.i(TAG, "unloadSoundModel(): id = " + soundModelId);
}
+ sEventLogger.log(new SoundTriggerLogger.StringEvent("unloadSoundModel(): id = "
+ + soundModelId));
+
synchronized (mLock) {
SoundModel soundModel = mLoadedModels.get(soundModelId.getUuid());
if (soundModel == null) {
Slog.e(TAG, soundModelId + " is not loaded");
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ "unloadSoundModel(): " + soundModelId + " is not loaded"));
+
return STATUS_ERROR;
}
int ret;
@@ -411,10 +503,18 @@ public class SoundTriggerService extends SystemService {
break;
default:
Slog.e(TAG, "Unknown model type");
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ "unloadSoundModel(): Unknown model type"));
+
return STATUS_ERROR;
}
if (ret != STATUS_OK) {
Slog.e(TAG, "Failed to unload model");
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ "unloadSoundModel(): Failed to unload model"));
+
return ret;
}
mLoadedModels.remove(soundModelId.getUuid());
@@ -444,10 +544,17 @@ public class SoundTriggerService extends SystemService {
Slog.i(TAG, "getModelState(): id = " + soundModelId);
}
+ sEventLogger.log(new SoundTriggerLogger.StringEvent("getModelState(): id = "
+ + soundModelId));
+
synchronized (mLock) {
SoundModel soundModel = mLoadedModels.get(soundModelId.getUuid());
if (soundModel == null) {
Slog.e(TAG, soundModelId + " is not loaded");
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent("getModelState(): "
+ + soundModelId + " is not loaded"));
+
return ret;
}
switch (soundModel.type) {
@@ -459,6 +566,10 @@ public class SoundTriggerService extends SystemService {
break;
default:
Slog.e(TAG, "Unknown model type");
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ "getModelState(): Unknown model type"));
+
break;
}
@@ -708,6 +819,10 @@ public class SoundTriggerService extends SystemService {
mService.removeClient(mPuuid);
} catch (Exception e) {
Slog.e(TAG, mPuuid + ": Cannot remove client", e);
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ + ": Cannot remove client"));
+
}
mService = null;
@@ -730,6 +845,8 @@ public class SoundTriggerService extends SystemService {
private void destroy() {
if (DEBUG) Slog.v(TAG, mPuuid + ": destroy");
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid + ": destroy"));
+
synchronized (mRemoteServiceLock) {
disconnectLocked();
@@ -761,6 +878,10 @@ public class SoundTriggerService extends SystemService {
} catch (Exception e) {
Slog.e(TAG, mPuuid + ": Could not stop operation "
+ mRunningOpIds.valueAt(i), e);
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ + ": Could not stop operation " + mRunningOpIds.valueAt(i)));
+
}
}
@@ -786,6 +907,10 @@ public class SoundTriggerService extends SystemService {
if (ri == null) {
Slog.w(TAG, mPuuid + ": " + mServiceName + " not found");
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ + ": " + mServiceName + " not found"));
+
return;
}
@@ -793,6 +918,11 @@ public class SoundTriggerService extends SystemService {
.equals(ri.serviceInfo.permission)) {
Slog.w(TAG, mPuuid + ": " + mServiceName + " does not require "
+ BIND_SOUND_TRIGGER_DETECTION_SERVICE);
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ + ": " + mServiceName + " does not require "
+ + BIND_SOUND_TRIGGER_DETECTION_SERVICE));
+
return;
}
@@ -803,6 +933,10 @@ public class SoundTriggerService extends SystemService {
mRemoteServiceWakeLock.acquire();
} else {
Slog.w(TAG, mPuuid + ": Could not bind to " + mServiceName);
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ + ": Could not bind to " + mServiceName));
+
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -821,6 +955,9 @@ public class SoundTriggerService extends SystemService {
Slog.w(TAG, mPuuid + ": Dropped operation as already destroyed or marked for "
+ "destruction");
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ + ":Dropped operation as already destroyed or marked for destruction"));
+
op.drop();
return;
}
@@ -847,11 +984,20 @@ public class SoundTriggerService extends SystemService {
if (DEBUG || opsAllowed + 10 > opsAdded) {
Slog.w(TAG, mPuuid + ": Dropped operation as too many operations "
+ "were run in last 24 hours");
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ + ": Dropped operation as too many operations "
+ + "were run in last 24 hours"));
+
}
op.drop();
} catch (Exception e) {
Slog.e(TAG, mPuuid + ": Could not drop operation", e);
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ + ": Could not drop operation"));
+
}
} else {
mNumOps.addOp(currentTime);
@@ -866,10 +1012,17 @@ public class SoundTriggerService extends SystemService {
try {
if (DEBUG) Slog.v(TAG, mPuuid + ": runOp " + opId);
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ + ": runOp " + opId));
+
op.run(opId, mService);
mRunningOpIds.add(opId);
} catch (Exception e) {
Slog.e(TAG, mPuuid + ": Could not run operation " + opId, e);
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ + ": Could not run operation " + opId));
+
}
}
@@ -897,6 +1050,10 @@ public class SoundTriggerService extends SystemService {
public void onKeyphraseDetected(SoundTrigger.KeyphraseRecognitionEvent event) {
Slog.w(TAG, mPuuid + "->" + mServiceName + ": IGNORED onKeyphraseDetected(" + event
+ ")");
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid + "->" + mServiceName
+ + ": IGNORED onKeyphraseDetected(" + event + ")"));
+
}
/**
@@ -928,6 +1085,8 @@ public class SoundTriggerService extends SystemService {
: AudioFormat.CHANNEL_IN_MONO,
captureFormat.getEncoding());
+ sEventLogger.log(new SoundTriggerLogger.StringEvent("createAudioRecordForEvent"));
+
return new AudioRecord(attributes, captureFormat, bufferSize,
event.getCaptureSession());
}
@@ -936,6 +1095,9 @@ public class SoundTriggerService extends SystemService {
public void onGenericSoundTriggerDetected(SoundTrigger.GenericRecognitionEvent event) {
if (DEBUG) Slog.v(TAG, mPuuid + ": Generic sound trigger event: " + event);
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ + ": Generic sound trigger event: " + event));
+
runOrAddOperation(new Operation(
// always execute:
() -> {
@@ -966,6 +1128,9 @@ public class SoundTriggerService extends SystemService {
public void onError(int status) {
if (DEBUG) Slog.v(TAG, mPuuid + ": onError: " + status);
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ + ": onError: " + status));
+
runOrAddOperation(
new Operation(
// always execute:
@@ -985,17 +1150,28 @@ public class SoundTriggerService extends SystemService {
@Override
public void onRecognitionPaused() {
Slog.i(TAG, mPuuid + "->" + mServiceName + ": IGNORED onRecognitionPaused");
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ + "->" + mServiceName + ": IGNORED onRecognitionPaused"));
+
}
@Override
public void onRecognitionResumed() {
Slog.i(TAG, mPuuid + "->" + mServiceName + ": IGNORED onRecognitionResumed");
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ + "->" + mServiceName + ": IGNORED onRecognitionResumed"));
+
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
if (DEBUG) Slog.v(TAG, mPuuid + ": onServiceConnected(" + service + ")");
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ + ": onServiceConnected(" + service + ")"));
+
synchronized (mRemoteServiceLock) {
mService = ISoundTriggerDetectionService.Stub.asInterface(service);
@@ -1016,6 +1192,9 @@ public class SoundTriggerService extends SystemService {
public void onServiceDisconnected(ComponentName name) {
if (DEBUG) Slog.v(TAG, mPuuid + ": onServiceDisconnected");
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ + ": onServiceDisconnected"));
+
synchronized (mRemoteServiceLock) {
mService = null;
}
@@ -1025,6 +1204,9 @@ public class SoundTriggerService extends SystemService {
public void onBindingDied(ComponentName name) {
if (DEBUG) Slog.v(TAG, mPuuid + ": onBindingDied");
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ + ": onBindingDied"));
+
synchronized (mRemoteServiceLock) {
destroy();
}
@@ -1034,6 +1216,9 @@ public class SoundTriggerService extends SystemService {
public void onNullBinding(ComponentName name) {
Slog.w(TAG, name + " for model " + mPuuid + " returned a null binding");
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(name + " for model "
+ + mPuuid + " returned a null binding"));
+
synchronized (mRemoteServiceLock) {
disconnectLocked();
}
@@ -1082,11 +1267,17 @@ public class SoundTriggerService extends SystemService {
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!isInitialized()) return;
mSoundTriggerHelper.dump(fd, pw, args);
+ // log
+ sEventLogger.dump(pw);
}
private synchronized boolean isInitialized() {
if (mSoundTriggerHelper == null ) {
Slog.e(TAG, "SoundTriggerHelper not initialized.");
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ "SoundTriggerHelper not initialized."));
+
return false;
}
return true;
@@ -1099,4 +1290,11 @@ public class SoundTriggerService extends SystemService {
throw new SecurityException("Caller does not hold the permission " + permission);
}
}
+
+ //=================================================================
+ // For logging
+
+ private static final SoundTriggerLogger sEventLogger = new SoundTriggerLogger(200,
+ "SoundTrigger activity");
+
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 2d8a280873f6..638733430ede 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3371,8 +3371,7 @@ public class CarrierConfigManager {
* May throw an {@link IllegalArgumentException} if {@code overrideValues} contains invalid
* values for the specified config keys.
*
- * NOTE: This API is meant for testing purposes only and may only be accessed from the shell UID
- * during instrumentation testing.
+ * NOTE: This API is meant for testing purposes only.
*
* @param subscriptionId The subscription ID for which the override should be done.
* @param overrideValues Key-value pairs of the values that are to be overridden. If set to
diff --git a/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp
index dfc3b6e15b3e..e556b0acb1a3 100644
--- a/tests/RollbackTest/Android.bp
+++ b/tests/RollbackTest/Android.bp
@@ -88,6 +88,15 @@ apex {
installable: false,
}
+apex {
+ name: "com.android.tests.rollback.testapex.RollbackTestApexV3",
+ manifest: "TestApex/RollbackTestApexV3.json",
+ file_contexts: "apex.test",
+ prebuilts: ["RollbackTestApex.prebuilt.txt"],
+ key: "RollbackTestApex.key",
+ installable: false,
+}
+
apex_key {
name: "RollbackTestApex.key",
public_key: "TestApex/com.android.tests.rollback.testapex.avbpubkey",
@@ -116,6 +125,7 @@ android_test {
":RollbackTestAppASplitV2",
":com.android.tests.rollback.testapex.RollbackTestApexV1",
":com.android.tests.rollback.testapex.RollbackTestApexV2",
+ ":com.android.tests.rollback.testapex.RollbackTestApexV3",
],
test_config: "RollbackTest.xml",
sdk_version: "test_current",
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
index 7e711c290e5a..3b0e2a56db3c 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
@@ -26,6 +26,7 @@ import android.content.rollback.RollbackManager;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import org.junit.After;
@@ -54,6 +55,8 @@ public class StagedRollbackTest {
"com.android.tests.rollback.testapex.RollbackTestApexV1.apex";
private static final String TEST_APEX_V2 =
"com.android.tests.rollback.testapex.RollbackTestApexV2.apex";
+ private static final String TEST_APEX_V3 =
+ "com.android.tests.rollback.testapex.RollbackTestApexV3.apex";
/**
* Adopts common shell permissions needed for rollback tests.
@@ -145,26 +148,13 @@ public class StagedRollbackTest {
/**
* Test rollbacks of staged installs an apk and an apex.
- * Prepare apex (and apk) phase.
- */
- @Test
- public void testApkAndApexPrepare() throws Exception {
- RollbackTestUtils.uninstall(TEST_APP_A);
- assertEquals(-1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
-
- // Note: can't uninstall the apex. See note in #testApexOnlyPrepareApex().
- RollbackTestUtils.installStaged(false, TEST_APP_A_V1, TEST_APEX_V1);
-
- // At this point, the host test driver will reboot the device and run
- // testApkAndApexEnableRollback().
- }
-
- /**
- * Test rollbacks of staged installs an apk and an apex.
* Enable rollback phase.
*/
@Test
public void testApkAndApexEnableRollback() throws Exception {
+ RollbackTestUtils.uninstall(TEST_APP_A);
+ RollbackTestUtils.install(TEST_APP_A_V1, false);
+
assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APEX_PKG));
assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
@@ -225,22 +215,6 @@ public class StagedRollbackTest {
/**
* Test rollbacks of staged installs involving only apex.
- * Prepare apex phase.
- */
- @Test
- public void testApexOnlyPrepareApex() throws Exception {
- // Note: We can't uninstall the apex if it is already on device,
- // because that isn't supported yet (b/123667725). As long as nothing
- // is failing, this should be fine because we don't expect the tests
- // to leave the device with v2 of the apex installed.
- RollbackTestUtils.installStaged(false, TEST_APEX_V1);
-
- // At this point, the host test driver will reboot the device and run
- // testApexOnlyEnableRollback().
- }
-
- /**
- * Test rollbacks of staged installs involving only apex.
* Enable rollback phase.
*/
@Test
@@ -291,4 +265,51 @@ public class StagedRollbackTest {
public void testApexOnlyConfirmRollback() throws Exception {
assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APEX_PKG));
}
+
+ /**
+ * Tests that apex update expires existing rollbacks for that apex.
+ * Enable rollback phase.
+ */
+ @Test
+ public void testApexRollbackExpirationEnableRollback() throws Exception {
+ assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APEX_PKG));
+ RollbackTestUtils.installStaged(true, TEST_APEX_V2);
+
+ // At this point, the host test driver will reboot the device and run
+ // testApexRollbackExpirationUpdateApex().
+ }
+
+ /**
+ * Tests that apex update expires existing rollbacks for that apex.
+ * Update apex phase.
+ */
+ @Test
+ public void testApexRollbackExpirationUpdateApex() throws Exception {
+ assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APEX_PKG));
+ RollbackTestUtils.installStaged(false, TEST_APEX_V3);
+
+ // At this point, the host test driver will reboot the device and run
+ // testApexRollbackExpirationConfirmExpiration().
+ }
+
+ /**
+ * Tests that apex update expires existing rollbacks for that apex.
+ * Confirm expiration phase.
+ */
+ @Test
+ public void testApexRollbackExpirationConfirmExpiration() throws Exception {
+ assertEquals(3, RollbackTestUtils.getInstalledVersion(TEST_APEX_PKG));
+
+ RollbackManager rm = RollbackTestUtils.getRollbackManager();
+ assertNull(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), TEST_APEX_PKG));
+ }
+
+ /**
+ * Helper function called by the host test to install v1 of the test apex,
+ * assuming the test apex is not installed.
+ */
+ @Test
+ public void installTestApexV1() throws Exception {
+ RollbackTestUtils.installStaged(false, TEST_APEX_V1);
+ }
}
diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
index ac7f634d51f1..1f87ed863034 100644
--- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
+++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
@@ -18,6 +18,7 @@ package com.android.tests.rollback.host;
import static org.junit.Assert.assertTrue;
+import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
@@ -30,6 +31,8 @@ import org.junit.runner.RunWith;
@RunWith(DeviceJUnit4ClassRunner.class)
public class StagedRollbackTest extends BaseHostJUnit4Test {
+ private static final String TEST_APEX_PKG = "com.android.tests.rollback.testapex";
+
/**
* Runs the given phase of a test by calling into the device.
* Throws an exception if the test phase fails.
@@ -59,8 +62,7 @@ public class StagedRollbackTest extends BaseHostJUnit4Test {
*/
@Test
public void testApexOnly() throws Exception {
- runPhase("testApexOnlyPrepareApex");
- getDevice().reboot();
+ installTestApexV1();
runPhase("testApexOnlyEnableRollback");
getDevice().reboot();
runPhase("testApexOnlyCommitRollback");
@@ -73,12 +75,45 @@ public class StagedRollbackTest extends BaseHostJUnit4Test {
*/
@Test
public void testApkAndApex() throws Exception {
- runPhase("testApkAndApexPrepare");
- getDevice().reboot();
+ installTestApexV1();
runPhase("testApkAndApexEnableRollback");
getDevice().reboot();
runPhase("testApkAndApexCommitRollback");
getDevice().reboot();
runPhase("testApkAndApexConfirmRollback");
}
+
+ /**
+ * Tests that apex update expires existing rollbacks for that apex.
+ */
+ @Test
+ public void testApexRollbackExpiration() throws Exception {
+ installTestApexV1();
+ runPhase("testApexRollbackExpirationEnableRollback");
+ getDevice().reboot();
+ runPhase("testApexRollbackExpirationUpdateApex");
+ getDevice().reboot();
+ runPhase("testApexRollbackExpirationConfirmExpiration");
+ }
+
+ /**
+ * Do whatever is necessary to get version 1 of the test apex installed on
+ * the device. Try to do so without extra reboots where possible to keep
+ * the test execution time down.
+ */
+ private void installTestApexV1() throws Exception {
+ for (ITestDevice.ApexInfo apexInfo : getDevice().getActiveApexes()) {
+ if (TEST_APEX_PKG.equals(apexInfo.name)) {
+ if (apexInfo.versionCode == 1) {
+ return;
+ }
+ getDevice().uninstallPackage(TEST_APEX_PKG);
+ getDevice().reboot();
+ break;
+ }
+ }
+
+ runPhase("installTestApexV1");
+ getDevice().reboot();
+ }
}
diff --git a/tests/RollbackTest/TestApex/RollbackTestApexV3.json b/tests/RollbackTest/TestApex/RollbackTestApexV3.json
new file mode 100644
index 000000000000..87a2c9dbcb6a
--- /dev/null
+++ b/tests/RollbackTest/TestApex/RollbackTestApexV3.json
@@ -0,0 +1,4 @@
+{
+ "name": "com.android.tests.rollback.testapex",
+ "version": 3
+}
diff --git a/tests/net/common/Android.bp b/tests/net/common/Android.bp
index 9ee58583779d..3b2e34a159ee 100644
--- a/tests/net/common/Android.bp
+++ b/tests/net/common/Android.bp
@@ -18,7 +18,7 @@
// They must be fast and stable, and exercise public or test APIs.
java_library {
name: "FrameworksNetCommonTests",
- srcs: ["java/**/*.java"],
+ srcs: ["java/**/*.java", "java/**/*.kt"],
static_libs: [
"androidx.test.rules",
"frameworks-net-testutils",
diff --git a/tests/net/common/java/android/net/LinkPropertiesTest.java b/tests/net/common/java/android/net/LinkPropertiesTest.java
index 417729150be8..709f5f69aa2b 100644
--- a/tests/net/common/java/android/net/LinkPropertiesTest.java
+++ b/tests/net/common/java/android/net/LinkPropertiesTest.java
@@ -868,12 +868,12 @@ public class LinkPropertiesTest {
source.setNat64Prefix(new IpPrefix("2001:db8:1:2:64:64::/96"));
- TestUtils.assertParcelingIsLossless(source, LinkProperties.CREATOR);
+ TestUtils.assertParcelingIsLossless(source);
}
@Test
public void testParcelUninitialized() throws Exception {
LinkProperties empty = new LinkProperties();
- TestUtils.assertParcelingIsLossless(empty, LinkProperties.CREATOR);
+ TestUtils.assertParcelingIsLossless(empty);
}
}
diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
index ad76388b3c9b..6bc7c1bb30f8 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
@@ -33,6 +33,8 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P;
import static android.net.NetworkCapabilities.RESTRICTED_CAPABILITIES;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_TEST;
+import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.NetworkCapabilities.UNRESTRICTED_CAPABILITIES;
@@ -585,4 +587,20 @@ public class NetworkCapabilitiesTest {
nc2.set(nc1); // Overwrites, as opposed to combineCapabilities
assertEquals(nc1, nc2);
}
+
+ @Test
+ public void testGetTransportTypes() {
+ final NetworkCapabilities nc = new NetworkCapabilities();
+ nc.addTransportType(TRANSPORT_CELLULAR);
+ nc.addTransportType(TRANSPORT_WIFI);
+ nc.addTransportType(TRANSPORT_VPN);
+ nc.addTransportType(TRANSPORT_TEST);
+
+ final int[] transportTypes = nc.getTransportTypes();
+ assertEquals(4, transportTypes.length);
+ assertEquals(TRANSPORT_CELLULAR, transportTypes[0]);
+ assertEquals(TRANSPORT_WIFI, transportTypes[1]);
+ assertEquals(TRANSPORT_VPN, transportTypes[2]);
+ assertEquals(TRANSPORT_TEST, transportTypes[3]);
+ }
}
diff --git a/tests/net/common/java/android/net/NetworkTest.java b/tests/net/common/java/android/net/NetworkTest.java
index 0bee7cd29d29..bef66b27df62 100644
--- a/tests/net/common/java/android/net/NetworkTest.java
+++ b/tests/net/common/java/android/net/NetworkTest.java
@@ -155,4 +155,12 @@ public class NetworkTest {
private static <T> void assertNotEqual(T t1, T t2) {
assertFalse(Objects.equals(t1, t2));
}
+
+ @Test
+ public void testGetPrivateDnsBypassingCopy() {
+ final Network copy = mNetwork.getPrivateDnsBypassingCopy();
+ assertEquals(mNetwork.netId, copy.netId);
+ assertNotEqual(copy.netId, copy.getNetIdForResolv());
+ assertNotEqual(mNetwork.getNetIdForResolv(), copy.getNetIdForResolv());
+ }
}
diff --git a/tests/net/common/java/android/net/StaticIpConfigurationTest.java b/tests/net/common/java/android/net/StaticIpConfigurationTest.java
index 8449ca76d580..5096be221cbf 100644
--- a/tests/net/common/java/android/net/StaticIpConfigurationTest.java
+++ b/tests/net/common/java/android/net/StaticIpConfigurationTest.java
@@ -31,7 +31,9 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import java.net.InetAddress;
+import java.util.ArrayList;
import java.util.HashSet;
+import java.util.List;
import java.util.Objects;
@RunWith(AndroidJUnit4.class)
@@ -46,6 +48,7 @@ public class StaticIpConfigurationTest {
private static final InetAddress DNS2 = IpAddress("8.8.4.4");
private static final InetAddress DNS3 = IpAddress("4.2.2.2");
private static final String IFACE = "eth0";
+ private static final String FAKE_DOMAINS = "google.com";
private static InetAddress IpAddress(String addr) {
return InetAddress.parseNumericAddress(addr);
@@ -69,7 +72,7 @@ public class StaticIpConfigurationTest {
s.dnsServers.add(DNS1);
s.dnsServers.add(DNS2);
s.dnsServers.add(DNS3);
- s.domains = "google.com";
+ s.domains = FAKE_DOMAINS;
return s;
}
@@ -178,8 +181,8 @@ public class StaticIpConfigurationTest {
expected.addDnsServer(DNS3);
assertEquals(expected, s.toLinkProperties(IFACE));
- s.domains = "google.com";
- expected.setDomains("google.com");
+ s.domains = FAKE_DOMAINS;
+ expected.setDomains(FAKE_DOMAINS);
assertEquals(expected, s.toLinkProperties(IFACE));
s.gateway = null;
@@ -218,4 +221,53 @@ public class StaticIpConfigurationTest {
StaticIpConfiguration s2 = passThroughParcel(s);
assertEquals(s, s2);
}
+
+ @Test
+ public void testBuilder() {
+ final ArrayList<InetAddress> dnsServers = new ArrayList<>();
+ dnsServers.add(DNS1);
+
+ final StaticIpConfiguration s = new StaticIpConfiguration.Builder()
+ .setIpAddress(ADDR)
+ .setGateway(GATEWAY)
+ .setDomains(FAKE_DOMAINS)
+ .setDnsServers(dnsServers)
+ .build();
+
+ assertEquals(s.ipAddress, s.getIpAddress());
+ assertEquals(ADDR, s.getIpAddress());
+ assertEquals(s.gateway, s.getGateway());
+ assertEquals(GATEWAY, s.getGateway());
+ assertEquals(s.domains, s.getDomains());
+ assertEquals(FAKE_DOMAINS, s.getDomains());
+ assertTrue(s.dnsServers.equals(s.getDnsServers()));
+ assertEquals(1, s.getDnsServers().size());
+ assertEquals(DNS1, s.getDnsServers().get(0));
+ }
+
+ @Test
+ public void testAddDnsServers() {
+ final StaticIpConfiguration s = new StaticIpConfiguration((StaticIpConfiguration) null);
+ checkEmpty(s);
+
+ s.addDnsServer(DNS1);
+ assertEquals(1, s.getDnsServers().size());
+ assertEquals(DNS1, s.getDnsServers().get(0));
+
+ s.addDnsServer(DNS2);
+ s.addDnsServer(DNS3);
+ assertEquals(3, s.getDnsServers().size());
+ assertEquals(DNS2, s.getDnsServers().get(1));
+ assertEquals(DNS3, s.getDnsServers().get(2));
+ }
+
+ @Test
+ public void testGetRoutes() {
+ final StaticIpConfiguration s = makeTestObject();
+ final List<RouteInfo> routeInfoList = s.getRoutes(IFACE);
+
+ assertEquals(2, routeInfoList.size());
+ assertEquals(new RouteInfo(ADDR, (InetAddress) null, IFACE), routeInfoList.get(0));
+ assertEquals(new RouteInfo((IpPrefix) null, GATEWAY, IFACE), routeInfoList.get(1));
+ }
}
diff --git a/tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java b/tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java
index 7238895b3657..3ed8a86b2fb5 100644
--- a/tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java
@@ -36,7 +36,7 @@ public class ApfCapabilitiesTest {
final ApfCapabilities caps = new ApfCapabilities(123, 456, 789);
ParcelableTestUtil.assertFieldCountEquals(3, ApfCapabilities.class);
- TestUtils.assertParcelingIsLossless(caps, ApfCapabilities.CREATOR);
+ TestUtils.assertParcelingIsLossless(caps);
}
@Test
diff --git a/tests/net/common/java/android/net/metrics/DhcpErrorEventTest.kt b/tests/net/common/java/android/net/metrics/DhcpErrorEventTest.kt
new file mode 100644
index 000000000000..e19195322e1d
--- /dev/null
+++ b/tests/net/common/java/android/net/metrics/DhcpErrorEventTest.kt
@@ -0,0 +1,67 @@
+package android.net.metrics
+
+import android.net.metrics.DhcpErrorEvent.errorCodeWithOption
+import android.net.metrics.DhcpErrorEvent.DHCP_INVALID_OPTION_LENGTH
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.internal.util.TestUtils.parcelingRoundTrip
+import java.lang.reflect.Modifier
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotNull
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+
+private const val TEST_ERROR_CODE = 12345
+/**
+ * DHCP Optional Type: DHCP Subnet Mask (Copy from DhcpPacket.java)
+ */
+private const val DHCP_SUBNET_MASK = 1
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class DhcpErrorEventTest {
+
+ @Test
+ fun testConstructor() {
+ val event = DhcpErrorEvent(TEST_ERROR_CODE)
+ assertEquals(TEST_ERROR_CODE, event.errorCode)
+ }
+
+ @Test
+ fun testParcelUnparcel() {
+ val event = DhcpErrorEvent(TEST_ERROR_CODE)
+ val parceled = parcelingRoundTrip(event)
+ assertEquals(TEST_ERROR_CODE, parceled.errorCode)
+ }
+
+ @Test
+ fun testErrorCodeWithOption() {
+ val errorCode = errorCodeWithOption(DHCP_INVALID_OPTION_LENGTH, DHCP_SUBNET_MASK);
+ assertTrue((DHCP_INVALID_OPTION_LENGTH and errorCode) == DHCP_INVALID_OPTION_LENGTH);
+ assertTrue((DHCP_SUBNET_MASK and errorCode) == DHCP_SUBNET_MASK);
+ }
+
+ @Test
+ fun testToString() {
+ val names = listOf("L2_ERROR", "L3_ERROR", "L4_ERROR", "DHCP_ERROR", "MISC_ERROR")
+ val errorFields = DhcpErrorEvent::class.java.declaredFields.filter {
+ it.type == Int::class.javaPrimitiveType
+ && Modifier.isPublic(it.modifiers) && Modifier.isStatic(it.modifiers)
+ && it.name !in names
+ }
+
+ errorFields.forEach {
+ val intValue = it.getInt(null)
+ val stringValue = DhcpErrorEvent(intValue).toString()
+ assertTrue("Invalid string for error 0x%08X (field %s): %s".format(intValue, it.name,
+ stringValue),
+ stringValue.contains(it.name))
+ }
+ }
+
+ @Test
+ fun testToString_InvalidErrorCode() {
+ assertNotNull(DhcpErrorEvent(TEST_ERROR_CODE).toString())
+ }
+} \ No newline at end of file
diff --git a/tests/net/java/android/net/TcpKeepalivePacketDataTest.java b/tests/net/java/android/net/TcpKeepalivePacketDataTest.java
index e0b722761c34..583d3fd536aa 100644
--- a/tests/net/java/android/net/TcpKeepalivePacketDataTest.java
+++ b/tests/net/java/android/net/TcpKeepalivePacketDataTest.java
@@ -79,7 +79,7 @@ public final class TcpKeepalivePacketDataTest {
assertEquals(testInfo.tos, resultData.ipTos);
assertEquals(testInfo.ttl, resultData.ipTtl);
- TestUtils.assertParcelingIsLossless(resultData, TcpKeepalivePacketData.CREATOR);
+ TestUtils.assertParcelingIsLossless(resultData);
final byte[] packet = resultData.getPacket();
// IP version and IHL
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index c15775fc4734..363ac9ce1d25 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -567,6 +567,16 @@ public class ConnectivityServiceTest {
protected void preventAutomaticReconnect() {
mPreventReconnectReceived.open();
}
+
+ @Override
+ protected void addKeepalivePacketFilter(Message msg) {
+ Log.i(TAG, "Add keepalive packet filter.");
+ }
+
+ @Override
+ protected void removeKeepalivePacketFilter(Message msg) {
+ Log.i(TAG, "Remove keepalive packet filter.");
+ }
};
assertEquals(mNetworkAgent.netId, nmNetworkCaptor.getValue().netId);
diff --git a/tests/net/util/java/com/android/internal/util/TestUtils.java b/tests/net/util/java/com/android/internal/util/TestUtils.java
index 75329a805606..a99cd4716f9a 100644
--- a/tests/net/util/java/com/android/internal/util/TestUtils.java
+++ b/tests/net/util/java/com/android/internal/util/TestUtils.java
@@ -69,9 +69,17 @@ public final class TestUtils {
}
}
- // TODO : fetch the creator through reflection or something instead of passing it
- public static <T extends Parcelable, C extends Parcelable.Creator<T>>
- void assertParcelingIsLossless(T source, C creator) {
+ /**
+ * Return a new instance of {@code T} after being parceled then unparceled.
+ */
+ public static <T extends Parcelable> T parcelingRoundTrip(T source) {
+ final Parcelable.Creator<T> creator;
+ try {
+ creator = (Parcelable.Creator<T>) source.getClass().getField("CREATOR").get(null);
+ } catch (IllegalAccessException | NoSuchFieldException e) {
+ fail("Missing CREATOR field: " + e.getMessage());
+ return null;
+ }
Parcel p = Parcel.obtain();
source.writeToParcel(p, /* flags */ 0);
p.setDataPosition(0);
@@ -79,7 +87,14 @@ public final class TestUtils {
p = Parcel.obtain();
p.unmarshall(marshalled, 0, marshalled.length);
p.setDataPosition(0);
- T dest = creator.createFromParcel(p);
- assertEquals(source, dest);
+ return creator.createFromParcel(p);
+ }
+
+ /**
+ * Assert that after being parceled then unparceled, {@code source} is equal to the original
+ * object.
+ */
+ public static <T extends Parcelable> void assertParcelingIsLossless(T source) {
+ assertEquals(source, parcelingRoundTrip(source));
}
}
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index 97ca6dc7bf34..f41426d84af1 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -2132,6 +2132,10 @@ def show_deprecations_at_birth(cur, prev):
# Remove all existing things so we're left with new
for prev_clazz in prev.values():
+ if prev_clazz.fullname not in cur:
+ # The class was removed this release; we can safely ignore it.
+ continue
+
cur_clazz = cur[prev_clazz.fullname]
if not is_interesting(cur_clazz): continue