Merge "Add Network, NetworkCapabilities, StaticIpConfiguration common test cases" into qt-dev
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 1433252..4c97c34 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -1202,9 +1202,10 @@
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 @@
// 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);
- if (trainVersionCode == -1 && readTrainInfoSuccess) {
- trainVersionCode = trainInfo.trainVersionCode;
+ 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;
+ }
+
+ // 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 @@
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 929d260..0b6df8b 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -189,8 +189,11 @@
* 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 ca874b5..0ade531 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -678,7 +678,8 @@
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/NetworkStack/src/android/net/apf/ApfFilter.java b/packages/NetworkStack/src/android/net/apf/ApfFilter.java
index 663e2f1..359c859 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.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 @@
}
/**
- * 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 @@
}
/**
+ * 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 96e09fa..dc74c04 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.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 @@
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 @@
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 @@
}
/**
- * 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 @@
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 a0e508f..93ab3be 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 @@
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 @@
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 @@
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 9b6ccae..90a7768 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 0000000..d7f67db
--- /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 0000000..e022c73
--- /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 7338687..4abe9f0 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 acc03c4..60d7126 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 & hold Back and Overview to unpin.</string>
<string name="screen_pinning_description_recents_invisible">This keeps it in view until you unpin. Touch & hold Back and Home to unpin.</string>
+ <string name="screen_pinning_description_gestural">This keeps it in view until you unpin. Swipe up & hold to unpin.</string>
<!-- Screen pinning dialog description. -->
<string name="screen_pinning_description_accessible">This keeps it in view until you unpin. Touch & hold Overview to unpin.</string>
<string name="screen_pinning_description_recents_invisible_accessible">This keeps it in view until you unpin. Touch & 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 6f44623..2c5fa60 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 @@
* 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 3ace705..5ed6a42 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_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 @@
}
@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 07391ed..ade903d 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 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 @@
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 @@
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 @@
mWindowManager.addView(mRequestWindow, lp);
}
+ @Override
+ public void onNavigationModeChanged(int mode) {
+ mNavBarMode = mode;
+ }
+
public void onConfigurationChanged() {
if (mRequestWindow != null) {
mRequestWindow.onConfigurationChanged();
@@ -224,7 +234,9 @@
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 @@
&& 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 5f5fad3..6a93c7c 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 @@
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 @@
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 541c142..5bc17f5 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 @@
* 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 @@
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 @@
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 02b660f..9e843f9 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 @@
/**
* 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 @@
* @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 404c07b..963fc54 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 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 @@
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 6f1e161..4333200 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.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 @@
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 @@
}
};
+ 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 @@
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 @@
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 @@
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 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 @@
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 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 @@
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 @@
mTransitionListener.onBackAltCleared();
}
mImeVisible = visible;
- updateWindowTouchable();
}
public void setDisabledFlags(int disabledFlags) {
@@ -564,7 +614,7 @@
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 @@
}
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 @@
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 @@
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 @@
} 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 @@
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 @@
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 e887f5b..7203e57 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.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 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 @@
() -> 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 @@
@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 @@
}
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 @@
}
}
+ @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 dd0c344..2bfc311 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.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 @@
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 @@
mAnimatedDrawable = (AnimatedVectorDrawable) mState.mChildState.newDrawable().mutate();
setDrawableBounds(mAnimatedDrawable);
}
+ mOvalBgPaint.setColor(mState.mDarkColor);
}
public void setDarkIntensity(float intensity) {
@@ -165,7 +169,12 @@
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 @@
return;
}
+ if (mState.mHasOvalBg) {
+ canvas.drawOval(new RectF(bounds), mOvalBgPaint);
+ }
+
if (mAnimatedDrawable != null) {
mAnimatedDrawable.draw(canvas);
} else {
@@ -379,14 +392,16 @@
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 @@
* @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 c837c9c..cb70a1f 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 @@
@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 @@
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 @@
@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 @@
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 @@
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 @@
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 @@
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 @@
}
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/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 6154726..db2c742 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.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 @@
// 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 @@
} else if (data.restoreUserDataInProgress) {
restoreInProgress.add(data);
}
+
+ for (PackageRollbackInfo info : data.info.getPackages()) {
+ if (info.isApex()) {
+ apexPackageNames.add(info.getPackageName());
+ }
+ }
}
}
}
@@ -634,6 +642,14 @@
}
}
+ 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 4ed07c3..8a834c8 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 @@
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 1e77264..9989c52 100644
--- a/services/net/java/android/net/ip/IIpClient.aidl
+++ b/services/net/java/android/net/ip/IIpClient.aidl
@@ -17,6 +17,7 @@
import android.net.ProxyInfo;
import android.net.ProvisioningConfigurationParcelable;
+import android.net.NattKeepalivePacketDataParcelable;
import android.net.TcpKeepalivePacketDataParcelable;
/** @hide */
@@ -33,4 +34,5 @@
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 0000000..73b4ce7
--- /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 697469a..9c4c099 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 @@
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 @@
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 @@
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 @@
if (DEBUG) {
Slog.i(TAG, "updateSoundModel(): model = " + soundModel);
}
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent("updateSoundModel(): model = "
+ + soundModel));
+
mDbHelper.updateGenericSoundModel(soundModel);
}
@@ -226,6 +245,10 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
break;
default:
Slog.e(TAG, "Unknown model type");
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ "getModelState(): Unknown model type"));
+
break;
}
@@ -708,6 +819,10 @@
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 @@
private void destroy() {
if (DEBUG) Slog.v(TAG, mPuuid + ": destroy");
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid + ": destroy"));
+
synchronized (mRemoteServiceLock) {
disconnectLocked();
@@ -761,6 +878,10 @@
} 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 @@
if (ri == null) {
Slog.w(TAG, mPuuid + ": " + mServiceName + " not found");
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ + ": " + mServiceName + " not found"));
+
return;
}
@@ -793,6 +918,11 @@
.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 @@
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 @@
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 @@
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 @@
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 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 @@
: 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 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 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 @@
@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 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 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 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 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 @@
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/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp
index dfc3b6e..e556b0a 100644
--- a/tests/RollbackTest/Android.bp
+++ b/tests/RollbackTest/Android.bp
@@ -88,6 +88,15 @@
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 @@
":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 7e711c2..3b0e2a5 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 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 @@
"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 @@
/**
* 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 @@
/**
* 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 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 ac7f634..1f87ed8 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 @@
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 @@
@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 @@
*/
@Test
public void testApexOnly() throws Exception {
- runPhase("testApexOnlyPrepareApex");
- getDevice().reboot();
+ installTestApexV1();
runPhase("testApexOnlyEnableRollback");
getDevice().reboot();
runPhase("testApexOnlyCommitRollback");
@@ -73,12 +75,45 @@
*/
@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 0000000..87a2c9d
--- /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/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index c15775f..363ac9c 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -567,6 +567,16 @@
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/tools/apilint/apilint.py b/tools/apilint/apilint.py
index 97ca6dc..f41426d 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -2132,6 +2132,10 @@
# 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