Merge "Revert "[DO NOT MERGE] Handle config override via settings correctly"" into qt-dev
diff --git a/cmds/idmap2/idmap2/Scan.cpp b/cmds/idmap2/idmap2/Scan.cpp
index 83c034b..21c6bd2 100644
--- a/cmds/idmap2/idmap2/Scan.cpp
+++ b/cmds/idmap2/idmap2/Scan.cpp
@@ -68,9 +68,10 @@
};
bool VendorIsQOrLater() {
- // STOPSHIP(b/119390857): Check api version once Q sdk version is finalized
- std::string version = android::base::GetProperty("ro.vndk.version", "Q");
- return version == "Q" || version == "q";
+ constexpr int kQSdkVersion = 29;
+ int version = std::atoi(android::base::GetProperty("ro.vndk.version", "29").data());
+ // If the string cannot be parsed, it is a development sdk codename.
+ return version >= kQSdkVersion || version == 0;
}
Result<std::unique_ptr<std::vector<std::string>>> FindApkFiles(const std::vector<std::string>& dirs,
diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp
index 0a9161d..9b48a02 100644
--- a/cmds/statsd/src/storage/StorageManager.cpp
+++ b/cmds/statsd/src/storage/StorageManager.cpp
@@ -442,7 +442,7 @@
if (erase_data) {
remove(fullPathName.c_str());
- } else if (output.mIsHistory && !isAdb) {
+ } else if (!output.mIsHistory && !isAdb) {
// This means a real data owner has called to get this data. But the config says it
// wants to keep a local history. So now this file must be renamed as a history file.
// So that next time, when owner calls getData() again, this data won't be uploaded
diff --git a/cmds/statsd/tests/storage/StorageManager_test.cpp b/cmds/statsd/tests/storage/StorageManager_test.cpp
index cae2f30..9e15e99 100644
--- a/cmds/statsd/tests/storage/StorageManager_test.cpp
+++ b/cmds/statsd/tests/storage/StorageManager_test.cpp
@@ -125,6 +125,105 @@
EXPECT_EQ("300_2000_123454_history", list[3].mFileName);
}
+const string testDir = "/data/misc/stats-data/";
+const string file1 = testDir + "2557169347_1066_1";
+const string file2 = testDir + "2557169349_1066_1";
+const string file1_history = file1 + "_history";
+const string file2_history = file2 + "_history";
+
+bool prepareLocalHistoryTestFiles() {
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(
+ open(file1.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR)));
+ if (fd != -1) {
+ dprintf(fd, "content");
+ } else {
+ return false;
+ }
+
+ android::base::unique_fd fd2(TEMP_FAILURE_RETRY(
+ open(file2.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR)));
+ if (fd2 != -1) {
+ dprintf(fd2, "content");
+ } else {
+ return false;
+ }
+ return true;
+}
+
+void clearLocalHistoryTestFiles() {
+ TEMP_FAILURE_RETRY(remove(file1.c_str()));
+ TEMP_FAILURE_RETRY(remove(file2.c_str()));
+ TEMP_FAILURE_RETRY(remove(file1_history.c_str()));
+ TEMP_FAILURE_RETRY(remove(file2_history.c_str()));
+}
+
+bool fileExist(string name) {
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(name.c_str(), O_RDONLY | O_CLOEXEC)));
+ return fd != -1;
+}
+
+/* The following AppendConfigReportTests test the 4 combinations of [whether erase data] [whether
+ * the caller is adb] */
+TEST(StorageManagerTest, AppendConfigReportTest1) {
+ EXPECT_TRUE(prepareLocalHistoryTestFiles());
+
+ ProtoOutputStream out;
+ StorageManager::appendConfigMetricsReport(ConfigKey(1066, 1), &out, false /*erase?*/,
+ false /*isAdb?*/);
+
+ EXPECT_FALSE(fileExist(file1));
+ EXPECT_FALSE(fileExist(file2));
+
+ EXPECT_TRUE(fileExist(file1_history));
+ EXPECT_TRUE(fileExist(file2_history));
+ clearLocalHistoryTestFiles();
+}
+
+TEST(StorageManagerTest, AppendConfigReportTest2) {
+ EXPECT_TRUE(prepareLocalHistoryTestFiles());
+
+ ProtoOutputStream out;
+ StorageManager::appendConfigMetricsReport(ConfigKey(1066, 1), &out, true /*erase?*/,
+ false /*isAdb?*/);
+
+ EXPECT_FALSE(fileExist(file1));
+ EXPECT_FALSE(fileExist(file2));
+ EXPECT_FALSE(fileExist(file1_history));
+ EXPECT_FALSE(fileExist(file2_history));
+
+ clearLocalHistoryTestFiles();
+}
+
+TEST(StorageManagerTest, AppendConfigReportTest3) {
+ EXPECT_TRUE(prepareLocalHistoryTestFiles());
+
+ ProtoOutputStream out;
+ StorageManager::appendConfigMetricsReport(ConfigKey(1066, 1), &out, false /*erase?*/,
+ true /*isAdb?*/);
+
+ EXPECT_TRUE(fileExist(file1));
+ EXPECT_TRUE(fileExist(file2));
+ EXPECT_FALSE(fileExist(file1_history));
+ EXPECT_FALSE(fileExist(file2_history));
+
+ clearLocalHistoryTestFiles();
+}
+
+TEST(StorageManagerTest, AppendConfigReportTest4) {
+ EXPECT_TRUE(prepareLocalHistoryTestFiles());
+
+ ProtoOutputStream out;
+ StorageManager::appendConfigMetricsReport(ConfigKey(1066, 1), &out, true /*erase?*/,
+ true /*isAdb?*/);
+
+ EXPECT_FALSE(fileExist(file1));
+ EXPECT_FALSE(fileExist(file2));
+ EXPECT_FALSE(fileExist(file1_history));
+ EXPECT_FALSE(fileExist(file2_history));
+
+ clearLocalHistoryTestFiles();
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 1e982bc..3a74f7d 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -5564,15 +5564,9 @@
private void handleConfigurationChanged(Configuration config, CompatibilityInfo compat) {
- int configDiff = 0;
+ int configDiff;
+ boolean equivalent;
- // This flag tracks whether the new configuration is fundamentally equivalent to the
- // existing configuration. This is necessary to determine whether non-activity
- // callbacks should receive notice when the only changes are related to non-public fields.
- // We do not gate calling {@link #performActivityConfigurationChanged} based on this flag
- // as that method uses the same check on the activity config override as well.
- final boolean equivalent = config != null && mConfiguration != null
- && (0 == mConfiguration.diffPublicOnly(config));
final Theme systemTheme = getSystemContext().getTheme();
final Theme systemUiTheme = getSystemUiContext().getTheme();
@@ -5590,6 +5584,13 @@
return;
}
+ // This flag tracks whether the new configuration is fundamentally equivalent to the
+ // existing configuration. This is necessary to determine whether non-activity callbacks
+ // should receive notice when the only changes are related to non-public fields.
+ // We do not gate calling {@link #performActivityConfigurationChanged} based on this
+ // flag as that method uses the same check on the activity config override as well.
+ equivalent = mConfiguration != null && (0 == mConfiguration.diffPublicOnly(config));
+
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle configuration changed: "
+ config);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 3adddc7..7a3609a 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2002,9 +2002,18 @@
mDisplay.getRealSize(size);
desiredWindowWidth = size.x;
desiredWindowHeight = size.y;
+ } else if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
+ || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
+ // For wrap content, we have to remeasure later on anyways. Use size consistent with
+ // below so we get best use of the measure cache.
+ desiredWindowWidth = dipToPx(config.screenWidthDp);
+ desiredWindowHeight = dipToPx(config.screenHeightDp);
} else {
- desiredWindowWidth = mWinFrame.width();
- desiredWindowHeight = mWinFrame.height();
+ // After addToDisplay, the frame contains the frameHint from window manager, which
+ // for most windows is going to be the same size as the result of relayoutWindow.
+ // Using this here allows us to avoid remeasuring after relayoutWindow
+ desiredWindowWidth = frame.width();
+ desiredWindowHeight = frame.height();
}
// We used to use the following condition to choose 32 bits drawing caches:
diff --git a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
index b9ed963..7af45fc 100644
--- a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
+++ b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.app;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
+
import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -64,8 +66,21 @@
String component = Settings.Secure.getString(getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT);
+
+ if (isGestureNavigateEnabled()) {
+ TextView promptPrologue = findViewById(R.id.accessibility_button_prompt_prologue);
+ promptPrologue.setText(isTouchExploreOn()
+ ? R.string.accessibility_gesture_3finger_prompt_text
+ : R.string.accessibility_gesture_prompt_text);
+ }
+
if (TextUtils.isEmpty(component)) {
TextView prompt = findViewById(R.id.accessibility_button_prompt);
+ if (isGestureNavigateEnabled()) {
+ prompt.setText(isTouchExploreOn()
+ ? R.string.accessibility_gesture_3finger_instructional_text
+ : R.string.accessibility_gesture_instructional_text);
+ }
prompt.setVisibility(View.VISIBLE);
}
@@ -91,6 +106,16 @@
});
}
+ private boolean isGestureNavigateEnabled() {
+ return NAV_BAR_MODE_GESTURAL == getResources().getInteger(
+ com.android.internal.R.integer.config_navBarInteractionMode);
+ }
+
+ private boolean isTouchExploreOn() {
+ return ((AccessibilityManager) getSystemService(Context.ACCESSIBILITY_SERVICE))
+ .isTouchExplorationEnabled();
+ }
+
private static List<AccessibilityButtonTarget> getServiceAccessibilityButtonTargets(
@NonNull Context context) {
AccessibilityManager ams = (AccessibilityManager) context.getSystemService(
@@ -177,4 +202,4 @@
return mDrawable;
}
}
-}
\ No newline at end of file
+}
diff --git a/core/res/res/layout/accessibility_button_chooser.xml b/core/res/res/layout/accessibility_button_chooser.xml
index 480defb..383780a 100644
--- a/core/res/res/layout/accessibility_button_chooser.xml
+++ b/core/res/res/layout/accessibility_button_chooser.xml
@@ -40,6 +40,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="56dp"
+ android:id="@+id/accessibility_button_prompt_prologue"
android:textAppearance="?attr/textAppearanceMedium"
android:text="@string/accessibility_button_prompt_text"
android:gravity="start|center_vertical"
diff --git a/core/res/res/values-night/themes_device_defaults.xml b/core/res/res/values-night/themes_device_defaults.xml
index 8ba2c60..93975a9 100644
--- a/core/res/res/values-night/themes_device_defaults.xml
+++ b/core/res/res/values-night/themes_device_defaults.xml
@@ -87,5 +87,7 @@
<style name="Theme.DeviceDefault.Resolver" parent="Theme.DeviceDefault.ResolverCommon">
<item name="windowLightNavigationBar">false</item>
+ <item name="colorBackgroundFloating">@android:color/GM2_grey_800</item>
+ <item name="textColorSecondary">@android:color/resolver_text_color_secondary_dark</item>
</style>
</resources>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 048f341..3b28265 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -218,4 +218,9 @@
<color name="chooser_row_divider">@color/list_divider_color_light</color>
<color name="chooser_gradient_background">@color/loading_gradient_background_color_light</color>
<color name="chooser_gradient_highlight">@color/loading_gradient_highlight_color_light</color>
+
+ <color name="GM2_grey_800">#ff3C4043</color>
+
+ <!-- Resolver/Chooser -->
+ <color name="resolver_text_color_secondary_dark">#ffC4C6C6</color>
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index e3f84c0..382e154 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4498,10 +4498,18 @@
<xliff:g id="service_name" example="TalkBack">%1$s</xliff:g></string>
<!-- Text appearing in a prompt at the top of UI allowing the user to select a target service or feature to be assigned to the Accessibility button in the navigation bar. -->
- <string name="accessibility_button_prompt_text">Choose a feature to use when you tap the Accessibility button:</string>
+ <string name="accessibility_button_prompt_text">Choose a service to use when you tap the accessibility button:</string>
+ <!-- Text appearing in a prompt at the top of UI allowing the user to select a target service or feature to be assigned to the Accessibility button when gesture navigation is enabled [CHAR LIMIT=none] -->
+ <string name="accessibility_gesture_prompt_text">Choose a service to use with the accessibility gesture (swipe up from the bottom of the screen with two fingers):</string>
+ <!-- Text appearing in a prompt at the top of UI allowing the user to select a target service or feature to be assigned to the Accessibility button when gesture navigation and TalkBack is enabled [CHAR LIMIT=none] -->
+ <string name="accessibility_gesture_3finger_prompt_text">Choose a service to use with the accessibility gesture (swipe up from the bottom of the screen with three fingers):</string>
<!-- Text describing how to display UI allowing a user to select a target service or feature to be assigned to the Accessibility button in the navigation bar. -->
- <string name="accessibility_button_instructional_text">To change features, touch & hold the Accessibility button.</string>
+ <string name="accessibility_button_instructional_text">To switch between services, touch & hold the accessibility button.</string>
+ <!-- Text describing how to display UI allowing a user to select a target service or feature to be assigned to the Accessibility button when gesture navigation is enabled. [CHAR LIMIT=none] -->
+ <string name="accessibility_gesture_instructional_text">To switch between services, swipe up with two fingers and hold.</string>
+ <!-- Text describing how to display UI allowing a user to select a target service or feature to be assigned to the Accessibility button when gesture navigation and TalkBack is enabled. [CHAR LIMIT=none] -->
+ <string name="accessibility_gesture_3finger_instructional_text">To switch between services, swipe up with three fingers and hold.</string>
<!-- Text used to describe system navigation features, shown within a UI allowing a user to assign system magnification features to the Accessibility button in the navigation bar. -->
<string name="accessibility_magnification_chooser_text">Magnification</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 3b3a062..27a4a46 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3286,9 +3286,16 @@
<java-symbol type="layout" name="accessibility_button_chooser_item" />
<java-symbol type="id" name="accessibility_button_chooser_grid" />
<java-symbol type="id" name="accessibility_button_prompt" />
+ <java-symbol type="id" name="accessibility_button_prompt_prologue" />
<java-symbol type="id" name="accessibility_button_target_icon" />
<java-symbol type="id" name="accessibility_button_target_label" />
<java-symbol type="string" name="accessibility_magnification_chooser_text" />
+
+ <java-symbol type="string" name="accessibility_gesture_prompt_text" />
+ <java-symbol type="string" name="accessibility_gesture_3finger_prompt_text" />
+ <java-symbol type="string" name="accessibility_gesture_instructional_text" />
+ <java-symbol type="string" name="accessibility_gesture_3finger_instructional_text" />
+
<java-symbol type="drawable" name="ic_accessibility_magnification" />
<!-- com.android.internal.widget.RecyclerView -->
diff --git a/packages/CarSystemUI/res/layout/car_volume_item.xml b/packages/CarSystemUI/res/layout/car_volume_item.xml
index 2275ca6..0b42904 100644
--- a/packages/CarSystemUI/res/layout/car_volume_item.xml
+++ b/packages/CarSystemUI/res/layout/car_volume_item.xml
@@ -19,16 +19,18 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:background="@color/car_volume_dialog_background_color"
+ android:paddingStart="@dimen/car_volume_item_padding_start"
+ android:paddingEnd="@dimen/car_volume_item_padding_end"
android:minHeight="@dimen/car_volume_item_height">
<!-- Primary Action. -->
<ImageView
android:id="@+id/primary_icon"
- android:layout_width="@dimen/car_primary_icon_size"
+ android:layout_width="@dimen/car_volume_item_icon_size"
android:layout_centerVertical="true"
- android:layout_marginStart="@dimen/car_volume_item_margin_horizontal"
android:layout_alignParentStart="true"
- android:layout_height="@dimen/car_primary_icon_size"/>
+ android:layout_height="@dimen/car_volume_item_icon_size"/>
<!-- Note: the horizontal padding and offset are set to 0 so that the track and thumb
aligns with the proper keylines. -->
@@ -61,11 +63,10 @@
android:background="@color/car_volume_item_divider_color"/>
<ImageView
android:id="@+id/supplemental_icon"
- android:layout_width="@dimen/car_primary_icon_size"
- android:layout_height="@dimen/car_primary_icon_size"
+ android:layout_width="@dimen/car_volume_item_icon_size"
+ android:layout_height="@dimen/car_volume_item_icon_size"
android:background="?android:attr/selectableItemBackground"
android:layout_centerVertical="true"
android:layout_alignParentEnd="true"
- android:layout_marginEnd="@dimen/car_volume_item_margin_horizontal"
android:scaleType="fitCenter"/>
</RelativeLayout>
diff --git a/packages/CarSystemUI/res/values/colors.xml b/packages/CarSystemUI/res/values/colors.xml
index dac5f2a..e13c940 100644
--- a/packages/CarSystemUI/res/values/colors.xml
+++ b/packages/CarSystemUI/res/values/colors.xml
@@ -37,6 +37,9 @@
<!-- The background color of the notification shade -->
<color name="notification_shade_background_color">#DD000000</color>
+ <!-- The background color of the car volume dialog -->
+ <color name="car_volume_dialog_background_color">@color/system_bar_background_opaque</color>
+
<!-- The color of the dividing line between grouped notifications. -->
<color name="notification_divider_color">@*android:color/notification_action_list</color>
diff --git a/packages/CarSystemUI/res/values/dimens.xml b/packages/CarSystemUI/res/values/dimens.xml
index 0358357b..7017484 100644
--- a/packages/CarSystemUI/res/values/dimens.xml
+++ b/packages/CarSystemUI/res/values/dimens.xml
@@ -78,8 +78,10 @@
<dimen name="ongoing_appops_chip_bg_corner_radius">12dp</dimen>
<!-- Car volume dimens. -->
+ <dimen name="car_volume_item_icon_size">@dimen/car_primary_icon_size</dimen>
<dimen name="car_volume_item_height">@*android:dimen/car_single_line_list_item_height</dimen>
- <dimen name="car_volume_item_margin_horizontal">@*android:dimen/car_keyline_1</dimen>
+ <dimen name="car_volume_item_padding_start">@*android:dimen/car_keyline_1</dimen>
+ <dimen name="car_volume_item_padding_end">@*android:dimen/car_keyline_1</dimen>
<dimen name="car_volume_item_seekbar_margin_vertical">@*android:dimen/car_padding_1</dimen>
<dimen name="car_volume_item_seekbar_margin_start">@*android:dimen/car_keyline_3</dimen>
<dimen name="car_volume_item_seekbar_margin_end">@*android:dimen/car_padding_4</dimen>
diff --git a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
index 94962f7..d0a63f0 100644
--- a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
+++ b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
@@ -32,7 +32,9 @@
import android.content.ServiceConnection;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
+import android.graphics.Color;
import android.graphics.PixelFormat;
+import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.os.Debug;
@@ -245,6 +247,7 @@
mExpanded = false;
mWindow = mDialog.getWindow();
mWindow.requestFeature(Window.FEATURE_NO_TITLE);
+ mWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND
| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
index f30de13..ea3c1d9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
@@ -110,13 +110,7 @@
}
public DataUsageInfo getDataUsageInfo() {
- final String subscriberId = getActiveSubscriberId();
- if (subscriberId == null) {
- return warn("no subscriber id");
- }
- NetworkTemplate template = NetworkTemplate.buildTemplateMobileAll(subscriberId);
- template = NetworkTemplate.normalize(template, getTelephonyManager()
- .getMergedSubscriberIds());
+ NetworkTemplate template = DataUsageUtils.getMobileTemplate(mContext, mSubscriptionId);
return getDataUsageInfo(template);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java
new file mode 100644
index 0000000..de38e8a
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java
@@ -0,0 +1,71 @@
+/*
+ * 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.settingslib.net;
+
+import android.content.Context;
+import android.net.NetworkTemplate;
+import android.os.ParcelUuid;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Utils class for data usage
+ */
+public class DataUsageUtils {
+ private static final String TAG = "DataUsageUtils";
+
+ /**
+ * Return mobile NetworkTemplate based on {@code subId}
+ */
+ public static NetworkTemplate getMobileTemplate(Context context, int subId) {
+ final TelephonyManager telephonyManager = context.getSystemService(
+ TelephonyManager.class);
+ final SubscriptionManager subscriptionManager = context.getSystemService(
+ SubscriptionManager.class);
+ final SubscriptionInfo info = subscriptionManager.getActiveSubscriptionInfo(subId);
+ final NetworkTemplate mobileAll = NetworkTemplate.buildTemplateMobileAll(
+ telephonyManager.getSubscriberId(subId));
+
+ if (info == null) {
+ Log.i(TAG, "Subscription is not active: " + subId);
+ return mobileAll;
+ }
+ final ParcelUuid groupUuid = info.getGroupUuid();
+ if (groupUuid == null) {
+ Log.i(TAG, "Subscription doesn't have valid group uuid: " + subId);
+ return mobileAll;
+ }
+
+ // Otherwise merge other subscriberId to create new NetworkTemplate
+ final List<SubscriptionInfo> groupInfos = subscriptionManager.getSubscriptionsInGroup(
+ groupUuid);
+ final List<String> mergedSubscriberIds = new ArrayList<>();
+ for (SubscriptionInfo subInfo : groupInfos) {
+ final String subscriberId = telephonyManager.getSubscriberId(
+ subInfo.getSubscriptionId());
+ if (subscriberId != null) {
+ mergedSubscriberIds.add(subscriberId);
+ }
+ }
+ return NetworkTemplate.normalize(mobileAll, mergedSubscriberIds.toArray(new String[0]));
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java
new file mode 100644
index 0000000..dc33cfe
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java
@@ -0,0 +1,110 @@
+/*
+ * 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.settingslib.net;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.net.NetworkTemplate;
+import android.os.ParcelUuid;
+import android.os.RemoteException;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+public class DataUsageUtilsTest {
+
+ private static final int SUB_ID = 1;
+ private static final int SUB_ID_2 = 2;
+ private static final String SUBSCRIBER_ID = "Test Subscriber";
+ private static final String SUBSCRIBER_ID_2 = "Test Subscriber 2";
+
+ @Mock
+ private TelephonyManager mTelephonyManager;
+ @Mock
+ private SubscriptionManager mSubscriptionManager;
+ @Mock
+ private SubscriptionInfo mInfo1;
+ @Mock
+ private SubscriptionInfo mInfo2;
+ @Mock
+ private ParcelUuid mParcelUuid;
+ private Context mContext;
+ private List<SubscriptionInfo> mInfos;
+
+ @Before
+ public void setUp() throws RemoteException {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = spy(RuntimeEnvironment.application);
+ when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
+ when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager);
+ when(mTelephonyManager.getSubscriberId(SUB_ID)).thenReturn(SUBSCRIBER_ID);
+ when(mTelephonyManager.getSubscriberId(SUB_ID_2)).thenReturn(SUBSCRIBER_ID_2);
+ when(mInfo1.getSubscriptionId()).thenReturn(SUB_ID);
+ when(mInfo2.getSubscriptionId()).thenReturn(SUB_ID_2);
+
+ mInfos = new ArrayList<>();
+ mInfos.add(mInfo1);
+ mInfos.add(mInfo2);
+ when(mSubscriptionManager.getSubscriptionsInGroup(mParcelUuid)).thenReturn(mInfos);
+ }
+
+ @Test
+ public void getMobileTemplate_infoNull_returnMobileAll() {
+ when(mSubscriptionManager.getActiveSubscriptionInfo(SUB_ID)).thenReturn(null);
+
+ final NetworkTemplate networkTemplate = DataUsageUtils.getMobileTemplate(mContext, SUB_ID);
+ assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID)).isTrue();
+ assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID_2)).isFalse();
+ }
+
+ @Test
+ public void getMobileTemplate_groupUuidNull_returnMobileAll() {
+ when(mSubscriptionManager.getActiveSubscriptionInfo(SUB_ID)).thenReturn(mInfo1);
+ when(mInfo1.getGroupUuid()).thenReturn(null);
+
+ final NetworkTemplate networkTemplate = DataUsageUtils.getMobileTemplate(mContext, SUB_ID);
+ assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID)).isTrue();
+ assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID_2)).isFalse();
+ }
+
+ @Test
+ public void getMobileTemplate_groupUuidExist_returnMobileMerged() {
+ when(mSubscriptionManager.getActiveSubscriptionInfo(SUB_ID)).thenReturn(mInfo1);
+ when(mInfo1.getGroupUuid()).thenReturn(mParcelUuid);
+
+ final NetworkTemplate networkTemplate = DataUsageUtils.getMobileTemplate(mContext, SUB_ID);
+ assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID)).isTrue();
+ assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID_2)).isTrue();
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index cc3a67c..240691b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -122,13 +122,21 @@
* disabled.
*/
public static boolean isAssistantGestureDisabled(int sysuiStateFlags) {
- // Disable when in screen pinning, immersive, the bouncer is showing, or the notifications
- // are interactive
+ // Disable when in screen pinning, immersive, the bouncer is showing
int disableFlags = SYSUI_STATE_SCREEN_PINNING
| SYSUI_STATE_NAV_BAR_HIDDEN
- | SYSUI_STATE_BOUNCER_SHOWING
- | SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
- return (sysuiStateFlags & disableFlags) != 0;
+ | SYSUI_STATE_BOUNCER_SHOWING;
+ if ((sysuiStateFlags & disableFlags) != 0) {
+ return true;
+ }
+
+ // Disable when notifications are showing (only if unlocked)
+ if ((sysuiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) != 0
+ && (sysuiStateFlags & SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING) == 0) {
+ return true;
+ }
+
+ return false;
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
index c26fdc2..2090748 100644
--- a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
+++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
@@ -324,25 +324,13 @@
final CharSequence[] carrierNames = new CharSequence[numSubs];
if (DEBUG) Log.d(TAG, "updateCarrierText(): " + numSubs);
- boolean anySimEmergency = mKeyguardUpdateMonitor.isAnySimEmergencyAble();
for (int i = 0; i < numSubs; i++) {
int subId = subs.get(i).getSubscriptionId();
carrierNames[i] = "";
subsIds[i] = subId;
subOrderBySlot[subs.get(i).getSimSlotIndex()] = i;
IccCardConstants.State simState = mKeyguardUpdateMonitor.getSimState(subId);
- ServiceState s = mKeyguardUpdateMonitor.getServiceState(subId);
CharSequence carrierName = subs.get(i).getCarrierName();
- // If this sub is showing No service but at least one slot currently supports emergency
- // calls, it should replace it by Emergency calls only
- if (s != null && s.getState() != ServiceState.STATE_IN_SERVICE && !s.isEmergencyOnly()
- && anySimEmergency) {
- carrierName = getContext().getText(
- com.android.internal.R.string.emergency_calls_only);
- if (DEBUG) {
- Log.d(TAG, "Subscription " + subId + "switched to ECO");
- }
- }
CharSequence carrierTextForSimState = getCarrierTextForSimState(simState, carrierName);
if (DEBUG) {
Log.d(TAG, "Handling (subId=" + subId + "): " + simState + " " + carrierName);
@@ -352,15 +340,16 @@
carrierNames[i] = carrierTextForSimState;
}
if (simState == IccCardConstants.State.READY) {
- if (s != null && s.getDataRegState() == ServiceState.STATE_IN_SERVICE) {
+ ServiceState ss = mKeyguardUpdateMonitor.mServiceStates.get(subId);
+ if (ss != null && ss.getDataRegState() == ServiceState.STATE_IN_SERVICE) {
// hack for WFC (IWLAN) not turning off immediately once
// Wi-Fi is disassociated or disabled
- if (s.getRilDataRadioTechnology() != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
+ if (ss.getRilDataRadioTechnology() != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
|| (mWifiManager.isWifiEnabled()
&& mWifiManager.getConnectionInfo() != null
&& mWifiManager.getConnectionInfo().getBSSID() != null)) {
if (DEBUG) {
- Log.d(TAG, "SIM ready and in service: subId=" + subId + ", ss=" + s);
+ Log.d(TAG, "SIM ready and in service: subId=" + subId + ", ss=" + ss);
}
anySimReadyAndInService = true;
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 446366b..6a4dbc8d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -195,12 +195,6 @@
HashMap<Integer, SimData> mSimDatas = new HashMap<Integer, SimData>();
HashMap<Integer, ServiceState> mServiceStates = new HashMap<Integer, ServiceState>();
- /**
- * Support up to 3 slots which is what's supported by {@link TelephonyManager#getPhoneCount}
- */
- private static final int SIM_SLOTS = 3;
- private final ServiceState[] mServiceStatesBySlot = new ServiceState[SIM_SLOTS];
-
private int mRingMode;
private int mPhoneState;
private boolean mKeyguardIsVisible;
@@ -332,7 +326,7 @@
handleAirplaneModeChanged();
break;
case MSG_SERVICE_STATE_CHANGE:
- handleServiceStateChange(msg.arg1, msg.arg2, (ServiceState) msg.obj);
+ handleServiceStateChange(msg.arg1, (ServiceState) msg.obj);
break;
case MSG_SCREEN_TURNED_ON:
handleScreenTurnedOn();
@@ -1044,13 +1038,12 @@
ServiceState serviceState = ServiceState.newFromBundle(intent.getExtras());
int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
- int slotId = intent.getIntExtra(PhoneConstants.SLOT_KEY, -1);
if (DEBUG) {
Log.v(TAG, "action " + action + " serviceState=" + serviceState + " subId="
+ subId);
}
- mHandler.obtainMessage(MSG_SERVICE_STATE_CHANGE, subId, slotId, serviceState)
- .sendToTarget();
+ mHandler.sendMessage(
+ mHandler.obtainMessage(MSG_SERVICE_STATE_CHANGE, subId, 0, serviceState));
} else if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(
action)) {
mHandler.sendEmptyMessage(MSG_DEVICE_POLICY_MANAGER_STATE_CHANGED);
@@ -2049,14 +2042,6 @@
*/
@VisibleForTesting
void handleServiceStateChange(int subId, ServiceState serviceState) {
- handleServiceStateChange(subId, -1, serviceState);
- }
-
- /**
- * Handle {@link #MSG_SERVICE_STATE_CHANGE}
- */
- @VisibleForTesting
- void handleServiceStateChange(int subId, int slotId, ServiceState serviceState) {
if (DEBUG) {
Log.d(TAG,
"handleServiceStateChange(subId=" + subId + ", serviceState=" + serviceState);
@@ -2070,7 +2055,6 @@
}
mServiceStates.put(subId, serviceState);
- if (slotId >= 0 && slotId < SIM_SLOTS) mServiceStatesBySlot[slotId] = serviceState;
for (int j = 0; j < mCallbacks.size(); j++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(j).get();
@@ -2296,21 +2280,6 @@
return mServiceStates.get(subId);
}
- /**
- * @return true iff at least one slot currently supports emergency calls
- */
- public boolean isAnySimEmergencyAble() {
- for (int i = 0; i < SIM_SLOTS; i++) {
- ServiceState s = mServiceStatesBySlot[i];
- if (s != null) {
- if (s.getState() == ServiceState.STATE_IN_SERVICE || s.isEmergencyOnly()) {
- return true;
- }
- }
- }
- return false;
- }
-
public void clearBiometricRecognized() {
mUserFingerprintAuthenticated.clear();
mUserFaceAuthenticated.clear();
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 4aaf85a..5e19219 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -199,6 +199,10 @@
return;
}
+ if (mOverlay == null || mBottomOverlay == null) {
+ return;
+ }
+
if (mAssistHintVisible != visible) {
mAssistHintVisible = visible;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
index 396a3a7..4a6c7d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
@@ -16,18 +16,26 @@
package com.android.systemui.statusbar.notification;
+import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
+
+import android.os.Handler;
+import android.os.SystemClock;
import android.view.View;
import androidx.collection.ArraySet;
+import com.android.systemui.Dumpable;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
import javax.inject.Inject;
+import javax.inject.Named;
import javax.inject.Singleton;
/**
@@ -35,14 +43,19 @@
* and reorder at the right time when they are out of view.
*/
@Singleton
-public class VisualStabilityManager implements OnHeadsUpChangedListener {
+public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpable {
+
+ private static final long TEMPORARY_REORDERING_ALLOWED_DURATION = 1000;
private final ArrayList<Callback> mCallbacks = new ArrayList<>();
+ private final Handler mHandler;
private NotificationPresenter mPresenter;
private boolean mPanelExpanded;
private boolean mScreenOn;
private boolean mReorderingAllowed;
+ private boolean mIsTemporaryReorderingAllowed;
+ private long mTemporaryReorderingStart;
private VisibilityLocationProvider mVisibilityLocationProvider;
private ArraySet<View> mAllowedReorderViews = new ArraySet<>();
private ArraySet<NotificationEntry> mLowPriorityReorderingViews = new ArraySet<>();
@@ -50,7 +63,12 @@
private boolean mPulsing;
@Inject
- public VisualStabilityManager(NotificationEntryManager notificationEntryManager) {
+ public VisualStabilityManager(
+ NotificationEntryManager notificationEntryManager,
+ @Named(MAIN_HANDLER_NAME) Handler handler) {
+
+ mHandler = handler;
+
notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
@Override
public void onPreEntryUpdated(NotificationEntry entry) {
@@ -114,10 +132,11 @@
}
private void updateReorderingAllowed() {
- boolean reorderingAllowed = (!mScreenOn || !mPanelExpanded) && !mPulsing;
- boolean changed = reorderingAllowed && !mReorderingAllowed;
+ boolean reorderingAllowed =
+ (!mScreenOn || !mPanelExpanded || mIsTemporaryReorderingAllowed) && !mPulsing;
+ boolean changedToTrue = reorderingAllowed && !mReorderingAllowed;
mReorderingAllowed = reorderingAllowed;
- if (changed) {
+ if (changedToTrue) {
notifyCallbacks();
}
}
@@ -180,6 +199,25 @@
}
/**
+ * Temporarily allows reordering of the entire shade for a period of 1000ms. Subsequent calls
+ * to this method will extend the timer.
+ */
+ public void temporarilyAllowReordering() {
+ mHandler.removeCallbacks(mOnTemporaryReorderingExpired);
+ mHandler.postDelayed(mOnTemporaryReorderingExpired, TEMPORARY_REORDERING_ALLOWED_DURATION);
+ if (!mIsTemporaryReorderingAllowed) {
+ mTemporaryReorderingStart = SystemClock.elapsedRealtime();
+ }
+ mIsTemporaryReorderingAllowed = true;
+ updateReorderingAllowed();
+ }
+
+ private final Runnable mOnTemporaryReorderingExpired = () -> {
+ mIsTemporaryReorderingAllowed = false;
+ updateReorderingAllowed();
+ };
+
+ /**
* Notify the visual stability manager that a new view was added and should be allowed to
* reorder next time.
*/
@@ -187,6 +225,20 @@
mAddedChildren.add(view);
}
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("VisualStabilityManager state:");
+ pw.print(" mIsTemporaryReorderingAllowed="); pw.println(mIsTemporaryReorderingAllowed);
+ pw.print(" mTemporaryReorderingStart="); pw.println(mTemporaryReorderingStart);
+
+ long now = SystemClock.elapsedRealtime();
+ pw.print(" Temporary reordering window has been open for ");
+ pw.print(now - (mIsTemporaryReorderingAllowed ? mTemporaryReorderingStart : now));
+ pw.println("ms");
+
+ pw.println();
+ }
+
public interface Callback {
/**
* Called when reordering is allowed again.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index f15d6b7..fe890fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -48,6 +48,7 @@
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -73,6 +74,7 @@
private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
private final Context mContext;
+ private final VisualStabilityManager mVisualStabilityManager;
private final AccessibilityManager mAccessibilityManager;
// Dependencies:
@@ -96,8 +98,11 @@
protected String mKeyToRemoveOnGutsClosed;
@Inject
- public NotificationGutsManager(Context context) {
+ public NotificationGutsManager(
+ Context context,
+ VisualStabilityManager visualStabilityManager) {
mContext = context;
+ mVisualStabilityManager = visualStabilityManager;
mAccessibilityManager = (AccessibilityManager)
mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
}
@@ -304,6 +309,7 @@
notificationInfoView.bindNotification(
pmUser,
iNotificationManager,
+ mVisualStabilityManager,
packageName,
row.getEntry().channel,
row.getUniqueChannels(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index 626701c..0f6740d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -65,6 +65,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.logging.NotificationCounters;
import java.lang.annotation.Retention;
@@ -104,6 +105,7 @@
private INotificationManager mINotificationManager;
private PackageManager mPm;
private MetricsLogger mMetricsLogger;
+ private VisualStabilityManager mVisualStabilityManager;
private ChannelEditorDialogController mChannelEditorDialogController;
private String mPackageName;
@@ -244,6 +246,7 @@
void bindNotification(
final PackageManager pm,
final INotificationManager iNotificationManager,
+ final VisualStabilityManager visualStabilityManager,
final String pkg,
final NotificationChannel notificationChannel,
final Set<NotificationChannel> uniqueChannelsInRow,
@@ -256,7 +259,7 @@
int importance,
boolean wasShownHighPriority)
throws RemoteException {
- bindNotification(pm, iNotificationManager, pkg, notificationChannel,
+ bindNotification(pm, iNotificationManager, visualStabilityManager, pkg, notificationChannel,
uniqueChannelsInRow, sbn, checkSaveListener, onSettingsClick,
onAppSettingsClick, isDeviceProvisioned, isNonblockable,
false /* isBlockingHelper */,
@@ -266,6 +269,7 @@
public void bindNotification(
PackageManager pm,
INotificationManager iNotificationManager,
+ VisualStabilityManager visualStabilityManager,
String pkg,
NotificationChannel notificationChannel,
Set<NotificationChannel> uniqueChannelsInRow,
@@ -281,6 +285,7 @@
throws RemoteException {
mINotificationManager = iNotificationManager;
mMetricsLogger = Dependency.get(MetricsLogger.class);
+ mVisualStabilityManager = visualStabilityManager;
mChannelEditorDialogController = Dependency.get(ChannelEditorDialogController.class);
mPackageName = pkg;
mUniqueChannelsInRow = uniqueChannelsInRow;
@@ -539,6 +544,7 @@
new UpdateImportanceRunnable(mINotificationManager, mPackageName, mAppUid,
mNumUniqueChannelsInRow == 1 ? mSingleNotificationChannel : null,
mStartingChannelImportance, newImportance));
+ mVisualStabilityManager.temporarilyAllowReordering();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
index fa81e40..db45ad78 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
@@ -27,7 +27,6 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
@@ -37,7 +36,6 @@
import android.net.ConnectivityManager;
import android.net.wifi.WifiManager;
import android.os.Handler;
-import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
@@ -45,7 +43,6 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import com.android.internal.R;
import com.android.internal.telephony.IccCardConstants;
import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
@@ -59,6 +56,7 @@
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
@SmallTest
@@ -70,7 +68,6 @@
private static final String TEST_CARRIER = "TEST_CARRIER";
private static final String TEST_CARRIER_2 = "TEST_CARRIER_2";
private static final String TEST_GROUP_UUID = "59b5c870-fc4c-47a4-a99e-9db826b48b24";
- private static final String EMERGENCY = "Emergency";
private static final int TEST_CARRIER_ID = 1;
private static final SubscriptionInfo TEST_SUBSCRIPTION = new SubscriptionInfo(0, "", 0,
TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "",
@@ -109,8 +106,6 @@
mContext.addMockSystemService(ConnectivityManager.class, mConnectivityManager);
mContext.addMockSystemService(TelephonyManager.class, mTelephonyManager);
mContext.addMockSystemService(SubscriptionManager.class, mSubscriptionManager);
- mContext.getOrCreateTestableResources().addOverride(
- R.string.emergency_calls_only, EMERGENCY);
mDependency.injectMockDependency(WakefulnessLifecycle.class);
mDependency.injectTestDependency(Dependency.MAIN_HANDLER,
new Handler(mTestableLooper.getLooper()));
@@ -195,6 +190,8 @@
when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(IccCardConstants.State.READY);
when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+ mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
+
ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
ArgumentCaptor.forClass(
CarrierTextController.CarrierTextCallbackInfo.class);
@@ -217,6 +214,8 @@
when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(IccCardConstants.State.READY);
when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+ mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
+
ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
ArgumentCaptor.forClass(
CarrierTextController.CarrierTextCallbackInfo.class);
@@ -260,6 +259,8 @@
when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(IccCardConstants.State.READY);
when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+ mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
+
ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
ArgumentCaptor.forClass(
CarrierTextController.CarrierTextCallbackInfo.class);
@@ -283,6 +284,8 @@
.thenReturn(IccCardConstants.State.NOT_READY);
when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+ mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
+
ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
ArgumentCaptor.forClass(
CarrierTextController.CarrierTextCallbackInfo.class);
@@ -306,6 +309,8 @@
.thenReturn(IccCardConstants.State.READY);
when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+ mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
+
ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
ArgumentCaptor.forClass(
CarrierTextController.CarrierTextCallbackInfo.class);
@@ -330,6 +335,7 @@
.thenReturn(IccCardConstants.State.NOT_READY)
.thenReturn(IccCardConstants.State.READY);
when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+ mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
ArgumentCaptor.forClass(
@@ -352,6 +358,7 @@
when(mKeyguardUpdateMonitor.getSimState(anyInt()))
.thenReturn(IccCardConstants.State.READY);
+ mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
mCarrierTextController.updateDisplayOpportunisticSubscriptionCarrierText(true);
when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(list);
@@ -366,127 +373,6 @@
assertEquals(TEST_CARRIER_2, captor.getValue().carrierText);
}
- @Test
- public void testCarrierText_replaceOutOfServiceWithEmergency() {
- reset(mCarrierTextCallback);
-
- List<SubscriptionInfo> list = new ArrayList<>();
- list.add(TEST_SUBSCRIPTION);
- when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
-
- when(mKeyguardUpdateMonitor.getSimState(anyInt()))
- .thenReturn(IccCardConstants.State.READY);
- ServiceState s = mock(ServiceState.class);
- when(mKeyguardUpdateMonitor.getServiceState(anyInt())).thenReturn(s);
- when(s.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
-
- when(mKeyguardUpdateMonitor.isAnySimEmergencyAble()).thenReturn(true);
-
- ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
- ArgumentCaptor.forClass(
- CarrierTextController.CarrierTextCallbackInfo.class);
-
- mCarrierTextController.updateCarrierText();
- mTestableLooper.processAllMessages();
- verify(mCarrierTextCallback).updateCarrierInfo(captor.capture());
-
- assertEquals(1, captor.getValue().listOfCarriers.length);
- assertEquals(EMERGENCY, captor.getValue().listOfCarriers[0]);
- }
-
- @Test
- public void testCarrierText_replaceOutOfServiceWithEmergencyOnlyInNoService() {
- reset(mCarrierTextCallback);
-
- List<SubscriptionInfo> list = new ArrayList<>();
- list.add(TEST_SUBSCRIPTION);
- list.add(TEST_SUBSCRIPTION_2);
- when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
-
- when(mKeyguardUpdateMonitor.getSimState(anyInt()))
- .thenReturn(IccCardConstants.State.READY);
- ServiceState sInService = mock(ServiceState.class);
- ServiceState sOutOfService = mock(ServiceState.class);
- when(mKeyguardUpdateMonitor.getServiceState(anyInt()))
- .thenReturn(sInService)
- .thenReturn(sOutOfService);
- when(sInService.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
- when(sOutOfService.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
-
- when(mKeyguardUpdateMonitor.isAnySimEmergencyAble()).thenReturn(true);
-
- ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
- ArgumentCaptor.forClass(
- CarrierTextController.CarrierTextCallbackInfo.class);
-
- mCarrierTextController.updateCarrierText();
- mTestableLooper.processAllMessages();
- verify(mCarrierTextCallback).updateCarrierInfo(captor.capture());
-
- assertEquals(2, captor.getValue().listOfCarriers.length);
- assertEquals(TEST_CARRIER, captor.getValue().listOfCarriers[0]);
- assertEquals(EMERGENCY, captor.getValue().listOfCarriers[1]);
- }
-
- @Test
- public void testCarrierText_dontReplaceWithEmergencyIfNotAble() {
- reset(mCarrierTextCallback);
-
- List<SubscriptionInfo> list = new ArrayList<>();
- list.add(TEST_SUBSCRIPTION);
- list.add(TEST_SUBSCRIPTION_2);
- when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
-
- when(mKeyguardUpdateMonitor.getSimState(anyInt()))
- .thenReturn(IccCardConstants.State.READY);
- ServiceState sOutOfService = mock(ServiceState.class);
- when(mKeyguardUpdateMonitor.getServiceState(anyInt())).thenReturn(sOutOfService);
- when(sOutOfService.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
-
- when(mKeyguardUpdateMonitor.isAnySimEmergencyAble()).thenReturn(false);
-
- ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
- ArgumentCaptor.forClass(
- CarrierTextController.CarrierTextCallbackInfo.class);
-
- mCarrierTextController.updateCarrierText();
- mTestableLooper.processAllMessages();
- verify(mCarrierTextCallback).updateCarrierInfo(captor.capture());
-
- assertEquals(2, captor.getValue().listOfCarriers.length);
- assertEquals(TEST_CARRIER, captor.getValue().listOfCarriers[0]);
- assertEquals(TEST_CARRIER_2, captor.getValue().listOfCarriers[1]);
- }
-
- @Test
- public void testCarrierText_dontReplaceWithEmergencyIfAlreadyEmergency() {
- reset(mCarrierTextCallback);
-
- List<SubscriptionInfo> list = new ArrayList<>();
- list.add(TEST_SUBSCRIPTION);
- when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
-
- when(mKeyguardUpdateMonitor.getSimState(anyInt()))
- .thenReturn(IccCardConstants.State.READY);
- ServiceState sOutOfService = mock(ServiceState.class);
- when(mKeyguardUpdateMonitor.getServiceState(anyInt())).thenReturn(sOutOfService);
- when(sOutOfService.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
- when(sOutOfService.isEmergencyOnly()).thenReturn(true);
-
- when(mKeyguardUpdateMonitor.isAnySimEmergencyAble()).thenReturn(false);
-
- ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
- ArgumentCaptor.forClass(
- CarrierTextController.CarrierTextCallbackInfo.class);
-
- mCarrierTextController.updateCarrierText();
- mTestableLooper.processAllMessages();
- verify(mCarrierTextCallback).updateCarrierInfo(captor.capture());
-
- assertEquals(1, captor.getValue().listOfCarriers.length);
- assertEquals(TEST_CARRIER, captor.getValue().listOfCarriers[0]);
- }
-
public static class TestCarrierTextController extends CarrierTextController {
private KeyguardUpdateMonitor mKUM;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 3a3cbad..6208ab8 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -22,7 +22,6 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
@@ -362,52 +361,6 @@
assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isTrue();
}
- @Test
- public void testAnySimEmergency_allSimsInService() {
- ServiceState s0 = mock(ServiceState.class);
- when(s0.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
-
- mKeyguardUpdateMonitor.handleServiceStateChange(0, 0, s0);
- assertThat(mKeyguardUpdateMonitor.isAnySimEmergencyAble()).isTrue();
- }
-
- @Test
- public void testAnySimEmergency_someSimsInServiceOthersNotECC() {
- ServiceState s0 = mock(ServiceState.class);
- when(s0.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
- ServiceState s1 = mock(ServiceState.class);
- when(s1.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
-
- mKeyguardUpdateMonitor.handleServiceStateChange(0, 0, s0);
- mKeyguardUpdateMonitor.handleServiceStateChange(0, 1, s1);
- assertThat(mKeyguardUpdateMonitor.isAnySimEmergencyAble()).isTrue();
- }
-
- @Test
- public void testAnySimEmergency_someSimsEmergencyCapable() {
- ServiceState s0 = mock(ServiceState.class);
- when(s0.getState()).thenReturn(ServiceState.STATE_POWER_OFF);
- ServiceState s1 = mock(ServiceState.class);
- when(s1.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
- when(s1.isEmergencyOnly()).thenReturn(true);
-
- mKeyguardUpdateMonitor.handleServiceStateChange(0, 0, s0);
- mKeyguardUpdateMonitor.handleServiceStateChange(0, 1, s1);
- assertThat(mKeyguardUpdateMonitor.isAnySimEmergencyAble()).isTrue();
- }
-
- @Test
- public void testAnySimEmergency_noEmergencyCapable() {
- ServiceState s0 = mock(ServiceState.class);
- when(s0.getState()).thenReturn(ServiceState.STATE_POWER_OFF);
- ServiceState s1 = mock(ServiceState.class);
- when(s1.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
-
- mKeyguardUpdateMonitor.handleServiceStateChange(0, 0, s0);
- mKeyguardUpdateMonitor.handleServiceStateChange(0, 1, s1);
- assertThat(mKeyguardUpdateMonitor.isAnySimEmergencyAble()).isFalse();
- }
-
private Intent putPhoneInfo(Intent intent, Bundle data, Boolean simInited) {
int subscription = simInited
? 1/* mock subid=1 */ : SubscriptionManager.DUMMY_SUBSCRIPTION_ID_BASE;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
index b35dcb6..dd2630b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
@@ -16,17 +16,20 @@
package com.android.systemui.statusbar.notification;
-import static junit.framework.Assert.assertEquals;
-
-import static org.mockito.Matchers.anyObject;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.os.Handler;
import android.service.notification.StatusBarNotification;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.NotificationPresenter;
@@ -38,11 +41,13 @@
import org.junit.runner.RunWith;
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper()
public class VisualStabilityManagerTest extends SysuiTestCase {
- private VisualStabilityManager mVisualStabilityManager = new VisualStabilityManager(
- mock(NotificationEntryManager.class));
+ private TestableLooper mTestableLooper;
+
+ private VisualStabilityManager mVisualStabilityManager;
private VisualStabilityManager.Callback mCallback = mock(VisualStabilityManager.Callback.class);
private VisibilityLocationProvider mLocationProvider = mock(VisibilityLocationProvider.class);
private ExpandableNotificationRow mRow = mock(ExpandableNotificationRow.class);
@@ -50,46 +55,53 @@
@Before
public void setUp() {
+ mTestableLooper = TestableLooper.get(this);
+ mVisualStabilityManager = new VisualStabilityManager(
+ mock(NotificationEntryManager.class),
+ new Handler(mTestableLooper.getLooper()));
+
mVisualStabilityManager.setUpWithPresenter(mock(NotificationPresenter.class));
mVisualStabilityManager.setVisibilityLocationProvider(mLocationProvider);
mEntry = new NotificationEntry(mock(StatusBarNotification.class));
mEntry.setRow(mRow);
+
+ when(mRow.getEntry()).thenReturn(mEntry);
}
@Test
public void testPanelExpansion() {
mVisualStabilityManager.setPanelExpanded(true);
mVisualStabilityManager.setScreenOn(true);
- assertEquals(mVisualStabilityManager.canReorderNotification(mRow), false);
+ assertFalse(mVisualStabilityManager.canReorderNotification(mRow));
mVisualStabilityManager.setPanelExpanded(false);
- assertEquals(mVisualStabilityManager.canReorderNotification(mRow), true);
+ assertTrue(mVisualStabilityManager.canReorderNotification(mRow));
}
@Test
public void testScreenOn() {
mVisualStabilityManager.setPanelExpanded(true);
mVisualStabilityManager.setScreenOn(true);
- assertEquals(mVisualStabilityManager.canReorderNotification(mRow), false);
+ assertFalse(mVisualStabilityManager.canReorderNotification(mRow));
mVisualStabilityManager.setScreenOn(false);
- assertEquals(mVisualStabilityManager.canReorderNotification(mRow), true);
+ assertTrue(mVisualStabilityManager.canReorderNotification(mRow));
}
@Test
public void testReorderingAllowedChangesScreenOn() {
mVisualStabilityManager.setPanelExpanded(true);
mVisualStabilityManager.setScreenOn(true);
- assertEquals(mVisualStabilityManager.isReorderingAllowed(), false);
+ assertFalse(mVisualStabilityManager.isReorderingAllowed());
mVisualStabilityManager.setScreenOn(false);
- assertEquals(mVisualStabilityManager.isReorderingAllowed(), true);
+ assertTrue(mVisualStabilityManager.isReorderingAllowed());
}
@Test
public void testReorderingAllowedChangesPanel() {
mVisualStabilityManager.setPanelExpanded(true);
mVisualStabilityManager.setScreenOn(true);
- assertEquals(mVisualStabilityManager.isReorderingAllowed(), false);
+ assertFalse(mVisualStabilityManager.isReorderingAllowed());
mVisualStabilityManager.setPanelExpanded(false);
- assertEquals(mVisualStabilityManager.isReorderingAllowed(), true);
+ assertTrue(mVisualStabilityManager.isReorderingAllowed());
}
@Test
@@ -126,51 +138,51 @@
mVisualStabilityManager.setPanelExpanded(true);
mVisualStabilityManager.setScreenOn(true);
mVisualStabilityManager.notifyViewAddition(mRow);
- assertEquals(mVisualStabilityManager.canReorderNotification(mRow), true);
+ assertTrue(mVisualStabilityManager.canReorderNotification(mRow));
}
@Test
public void testReorderingVisibleHeadsUpNotAllowed() {
mVisualStabilityManager.setPanelExpanded(true);
mVisualStabilityManager.setScreenOn(true);
- when(mLocationProvider.isInVisibleLocation(anyObject())).thenReturn(true);
+ when(mLocationProvider.isInVisibleLocation(any(NotificationEntry.class))).thenReturn(true);
mVisualStabilityManager.onHeadsUpStateChanged(mEntry, true);
- assertEquals(mVisualStabilityManager.canReorderNotification(mRow), false);
+ assertFalse(mVisualStabilityManager.canReorderNotification(mRow));
}
@Test
public void testReorderingVisibleHeadsUpAllowed() {
mVisualStabilityManager.setPanelExpanded(true);
mVisualStabilityManager.setScreenOn(true);
- when(mLocationProvider.isInVisibleLocation(anyObject())).thenReturn(false);
+ when(mLocationProvider.isInVisibleLocation(any(NotificationEntry.class))).thenReturn(false);
mVisualStabilityManager.onHeadsUpStateChanged(mEntry, true);
- assertEquals(mVisualStabilityManager.canReorderNotification(mRow), true);
+ assertTrue(mVisualStabilityManager.canReorderNotification(mRow));
}
@Test
public void testReorderingVisibleHeadsUpAllowedOnce() {
mVisualStabilityManager.setPanelExpanded(true);
mVisualStabilityManager.setScreenOn(true);
- when(mLocationProvider.isInVisibleLocation(anyObject())).thenReturn(false);
+ when(mLocationProvider.isInVisibleLocation(any(NotificationEntry.class))).thenReturn(false);
mVisualStabilityManager.onHeadsUpStateChanged(mEntry, true);
mVisualStabilityManager.onReorderingFinished();
- assertEquals(mVisualStabilityManager.canReorderNotification(mRow), false);
+ assertFalse(mVisualStabilityManager.canReorderNotification(mRow));
}
@Test
public void testPulsing() {
mVisualStabilityManager.setPulsing(true);
- assertEquals(mVisualStabilityManager.canReorderNotification(mRow), false);
+ assertFalse(mVisualStabilityManager.canReorderNotification(mRow));
mVisualStabilityManager.setPulsing(false);
- assertEquals(mVisualStabilityManager.canReorderNotification(mRow), true);
+ assertTrue(mVisualStabilityManager.canReorderNotification(mRow));
}
@Test
public void testReorderingAllowedChanges_Pulsing() {
mVisualStabilityManager.setPulsing(true);
- assertEquals(mVisualStabilityManager.isReorderingAllowed(), false);
+ assertFalse(mVisualStabilityManager.isReorderingAllowed());
mVisualStabilityManager.setPulsing(false);
- assertEquals(mVisualStabilityManager.isReorderingAllowed(), true);
+ assertTrue(mVisualStabilityManager.isReorderingAllowed());
}
@Test
@@ -180,4 +192,49 @@
mVisualStabilityManager.setPulsing(false);
verify(mCallback).onReorderingAllowed();
}
+
+ @Test
+ public void testTemporarilyAllowReorderingNotifiesCallbacks() {
+ // GIVEN having the panel open (which would block reordering)
+ mVisualStabilityManager.setScreenOn(true);
+ mVisualStabilityManager.setPanelExpanded(true);
+ mVisualStabilityManager.addReorderingAllowedCallback(mCallback);
+
+ // WHEN we temprarily allow reordering
+ mVisualStabilityManager.temporarilyAllowReordering();
+
+ // THEN callbacks are notified that reordering is allowed
+ verify(mCallback).onReorderingAllowed();
+ assertTrue(mVisualStabilityManager.isReorderingAllowed());
+ }
+
+ @Test
+ public void testTemporarilyAllowReorderingDoesntOverridePulsing() {
+ // GIVEN we are in a pulsing state
+ mVisualStabilityManager.setPulsing(true);
+ mVisualStabilityManager.addReorderingAllowedCallback(mCallback);
+
+ // WHEN we temprarily allow reordering
+ mVisualStabilityManager.temporarilyAllowReordering();
+
+ // THEN reordering is still not allowed
+ verify(mCallback, never()).onReorderingAllowed();
+ assertFalse(mVisualStabilityManager.isReorderingAllowed());
+ }
+
+ @Test
+ public void testTemporarilyAllowReorderingExpires() {
+ // GIVEN having the panel open (which would block reordering)
+ mVisualStabilityManager.setScreenOn(true);
+ mVisualStabilityManager.setPanelExpanded(true);
+ mVisualStabilityManager.addReorderingAllowedCallback(mCallback);
+
+ // WHEN we temprarily allow reordering and then wait until the window expires
+ mVisualStabilityManager.temporarilyAllowReordering();
+ assertTrue(mVisualStabilityManager.isReorderingAllowed());
+ mTestableLooper.processMessages(1);
+
+ // THEN reordering is no longer allowed
+ assertFalse(mVisualStabilityManager.isReorderingAllowed());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index 6376bd3..ef13b61 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -63,6 +63,7 @@
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
@@ -97,6 +98,7 @@
@Rule public MockitoRule mockito = MockitoJUnit.rule();
@Mock private MetricsLogger mMetricsLogger;
+ @Mock private VisualStabilityManager mVisualStabilityManager;
@Mock private NotificationPresenter mPresenter;
@Mock private NotificationActivityStarter mNotificationActivityStarter;
@Mock private NotificationStackScrollLayout mStackScroller;
@@ -111,11 +113,12 @@
mDependency.injectTestDependency(DeviceProvisionedController.class,
mDeviceProvisionedController);
mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
+ mDependency.injectTestDependency(VisualStabilityManager.class, mVisualStabilityManager);
mHandler = Handler.createAsync(mTestableLooper.getLooper());
mHelper = new NotificationTestHelper(mContext);
- mGutsManager = new NotificationGutsManager(mContext);
+ mGutsManager = new NotificationGutsManager(mContext, mVisualStabilityManager);
mGutsManager.setUpWithPresenter(mPresenter, mStackScroller,
mCheckSaveListener, mOnSettingsClickListener);
mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
@@ -316,6 +319,7 @@
verify(notificationInfoView).bindNotification(
any(PackageManager.class),
any(INotificationManager.class),
+ eq(mVisualStabilityManager),
eq(statusBarNotification.getPackageName()),
any(NotificationChannel.class),
anySet(),
@@ -344,6 +348,7 @@
verify(notificationInfoView).bindNotification(
any(PackageManager.class),
any(INotificationManager.class),
+ eq(mVisualStabilityManager),
eq(statusBarNotification.getPackageName()),
any(NotificationChannel.class),
anySet(),
@@ -374,6 +379,7 @@
verify(notificationInfoView).bindNotification(
any(PackageManager.class),
any(INotificationManager.class),
+ eq(mVisualStabilityManager),
eq(statusBarNotification.getPackageName()),
any(NotificationChannel.class),
anySet(),
@@ -403,6 +409,7 @@
verify(notificationInfoView).bindNotification(
any(PackageManager.class),
any(INotificationManager.class),
+ eq(mVisualStabilityManager),
eq(statusBarNotification.getPackageName()),
any(NotificationChannel.class),
anySet(),
@@ -431,6 +438,7 @@
verify(notificationInfoView).bindNotification(
any(PackageManager.class),
any(INotificationManager.class),
+ eq(mVisualStabilityManager),
eq(statusBarNotification.getPackageName()),
any(NotificationChannel.class),
anySet(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
index 78970d9..703adf7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
@@ -72,6 +72,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
import org.junit.After;
import org.junit.Before;
@@ -116,6 +117,8 @@
private PackageManager mMockPackageManager;
@Mock
private NotificationBlockingHelperManager mBlockingHelperManager;
+ @Mock
+ private VisualStabilityManager mVisualStabilityManager;
@Before
public void setUp() throws Exception {
@@ -193,11 +196,21 @@
@Test
public void testBindNotification_SetsTextApplicationName() throws Exception {
when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn,
- null, null, null,
- true, false,
- IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
final TextView textView = mNotificationInfo.findViewById(R.id.pkgname);
assertTrue(textView.getText().toString().contains("App Name"));
assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
@@ -208,20 +221,42 @@
final Drawable iconDrawable = mock(Drawable.class);
when(mMockPackageManager.getApplicationIcon(any(ApplicationInfo.class)))
.thenReturn(iconDrawable);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn,
- null, null, null, true, false,
- IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
final ImageView iconView = mNotificationInfo.findViewById(R.id.pkgicon);
assertEquals(iconDrawable, iconView.getDrawable());
}
@Test
public void testBindNotification_noDelegate() throws Exception {
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn,
- null, null, null, true, false,
- IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
assertEquals(GONE, nameView.getVisibility());
final TextView dividerView = mNotificationInfo.findViewById(R.id.pkg_divider);
@@ -238,10 +273,21 @@
applicationInfo);
when(mMockPackageManager.getApplicationLabel(any())).thenReturn("Other");
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, false,
- IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
assertEquals(VISIBLE, nameView.getVisibility());
assertTrue(nameView.getText().toString().contains("Proxied"));
@@ -251,10 +297,21 @@
@Test
public void testBindNotification_GroupNameHiddenIfNoGroup() throws Exception {
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, false,
- IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
assertEquals(GONE, groupNameView.getVisibility());
}
@@ -267,10 +324,21 @@
when(mMockINotificationManager.getNotificationChannelGroupForPackage(
eq("test_group_id"), eq(TEST_PACKAGE_NAME), eq(TEST_UID)))
.thenReturn(notificationChannelGroup);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, false,
- IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
assertEquals(View.VISIBLE, groupNameView.getVisibility());
assertEquals("Test Group Name", groupNameView.getText());
@@ -278,19 +346,42 @@
@Test
public void testBindNotification_SetsTextChannelName() throws Exception {
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, false,
- IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(TEST_CHANNEL_NAME, textView.getText());
}
@Test
public void testBindNotification_DefaultChannelDoesNotUseChannelName() throws Exception {
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mDefaultNotificationChannel, mDefaultNotificationChannelSet,
- mSbn, null, null, null, true, false, IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mDefaultNotificationChannel,
+ mDefaultNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(GONE, textView.getVisibility());
}
@@ -301,30 +392,64 @@
// Package has one channel by default.
when(mMockINotificationManager.getNumNotificationChannelsForPackage(
eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(10);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mDefaultNotificationChannel, mDefaultNotificationChannelSet,
- mSbn, null, null, null, true,
- false, IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mDefaultNotificationChannel,
+ mDefaultNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(VISIBLE, textView.getVisibility());
}
@Test
public void testBindNotification_UnblockablePackageUsesChannelName() throws Exception {
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, true,
- IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ true,
+ IMPORTANCE_DEFAULT,
+ true);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(VISIBLE, textView.getVisibility());
}
@Test
public void testBindNotification_BlockLink_BlockingHelper() throws Exception {
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, mock(
- NotificationInfo.OnSettingsClickListener.class), null, true, false,
- true /* isBlockingHelper */, IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ mock(NotificationInfo.OnSettingsClickListener.class),
+ null,
+ true,
+ false,
+ true /* isBlockingHelper */,
+ IMPORTANCE_DEFAULT,
+ true);
final View block =
mNotificationInfo.findViewById(R.id.blocking_helper_turn_off_notifications);
final View interruptivenessSettings = mNotificationInfo.findViewById(
@@ -336,12 +461,24 @@
@Test
public void testBindNotification_SetsOnClickListenerForSettings() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null,
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
(View v, NotificationChannel c, int appUid) -> {
assertEquals(mNotificationChannel, c);
latch.countDown();
- }, null, true, false, IMPORTANCE_DEFAULT, true);
+ },
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
settingsButton.performClick();
@@ -351,10 +488,21 @@
@Test
public void testBindNotification_SettingsButtonInvisibleWhenNoClickListener() throws Exception {
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, false,
- IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertTrue(settingsButton.getVisibility() != View.VISIBLE);
}
@@ -362,36 +510,80 @@
@Test
public void testBindNotification_SettingsButtonInvisibleWhenDeviceUnprovisioned()
throws Exception {
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null,
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
(View v, NotificationChannel c, int appUid) -> {
assertEquals(mNotificationChannel, c);
- }, null, false, false, IMPORTANCE_DEFAULT, true);
+ },
+ null,
+ false,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertTrue(settingsButton.getVisibility() != View.VISIBLE);
}
@Test
public void testBindNotification_SettingsButtonReappearsAfterSecondBind() throws Exception {
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, false,
- IMPORTANCE_DEFAULT, true);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null,
- (View v, NotificationChannel c, int appUid) -> {
- }, null, true, false, IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ (View v, NotificationChannel c, int appUid) -> { },
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertEquals(View.VISIBLE, settingsButton.getVisibility());
}
@Test
public void testBindNotificationLogging_notBlockingHelper() throws Exception {
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn,
- null, null, null,
- true, false,
- IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
verify(mMetricsLogger).write(argThat(logMaker ->
logMaker.getCategory() == MetricsEvent.ACTION_NOTE_CONTROLS
&& logMaker.getType() == MetricsEvent.TYPE_OPEN
@@ -401,12 +593,22 @@
@Test
public void testBindNotificationLogging_BlockingHelper() throws Exception {
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn,
- null, null, null,
- false, true,
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ false,
true,
- IMPORTANCE_DEFAULT, true);
+ true,
+ IMPORTANCE_DEFAULT,
+ true);
verify(mMetricsLogger).write(argThat(logMaker ->
logMaker.getCategory() == MetricsEvent.ACTION_NOTE_CONTROLS
&& logMaker.getType() == MetricsEvent.TYPE_OPEN
@@ -416,12 +618,22 @@
@Test
public void testLogBlockingHelperCounter_logsForBlockingHelper() throws Exception {
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn,
- null, null, null,
- false, true,
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ false,
true,
- IMPORTANCE_DEFAULT, true);
+ true,
+ IMPORTANCE_DEFAULT,
+ true);
mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent");
verify(mMetricsLogger).count(eq("HowCanNotifsBeRealIfAppsArent"), eq(1));
}
@@ -429,13 +641,23 @@
@Test
public void testOnClickListenerPassesNullChannelForBundle() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
TEST_PACKAGE_NAME, mNotificationChannel,
- createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT), mSbn, null,
+ createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT),
+ mSbn,
+ null,
(View v, NotificationChannel c, int appUid) -> {
assertEquals(null, c);
latch.countDown();
- }, null, true, true, IMPORTANCE_DEFAULT, true);
+ },
+ null,
+ true,
+ true,
+ IMPORTANCE_DEFAULT,
+ true);
mNotificationInfo.findViewById(R.id.info).performClick();
// Verify that listener was triggered.
@@ -446,10 +668,21 @@
@UiThreadTest
public void testBindNotification_ChannelNameInvisibleWhenBundleFromDifferentChannels()
throws Exception {
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel,
- createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT), mSbn, null, null,
- null, true, false, IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT),
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
final TextView channelNameView =
mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(GONE, channelNameView.getVisibility());
@@ -458,10 +691,21 @@
@Test
@UiThreadTest
public void testStopInvisibleIfBundleFromDifferentChannels() throws Exception {
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel,
- createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT), mSbn, null, null,
- null, true, false, IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT),
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
assertEquals(GONE, mNotificationInfo.findViewById(
R.id.interruptiveness_settings).getVisibility());
assertEquals(VISIBLE, mNotificationInfo.findViewById(
@@ -470,10 +714,21 @@
@Test
public void testBindNotification_whenAppUnblockable() throws Exception {
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, true,
- IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ true,
+ IMPORTANCE_DEFAULT,
+ true);
final TextView view = mNotificationInfo.findViewById(R.id.non_configurable_text);
assertEquals(View.VISIBLE, view.getVisibility());
assertEquals(mContext.getString(R.string.notification_unblockable_desc),
@@ -484,10 +739,21 @@
@Test
public void testBindNotification_DoesNotUpdateNotificationChannel() throws Exception {
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, false,
- IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
mTestableLooper.processAllMessages();
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
anyString(), eq(TEST_UID), any());
@@ -496,10 +762,21 @@
@Test
public void testDoesNotUpdateNotificationChannelAfterImportanceChanged() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, false,
- IMPORTANCE_LOW, false);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_LOW,
+ false);
mNotificationInfo.findViewById(R.id.alert).performClick();
mTestableLooper.processAllMessages();
@@ -511,10 +788,21 @@
public void testDoesNotUpdateNotificationChannelAfterImportanceChangedSilenced()
throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, false,
- IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
mNotificationInfo.findViewById(R.id.silence).performClick();
mTestableLooper.processAllMessages();
@@ -526,10 +814,21 @@
public void testHandleCloseControls_DoesNotUpdateNotificationChannelIfUnchanged()
throws Exception {
int originalImportance = mNotificationChannel.getImportance();
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, false,
- IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
mNotificationInfo.handleCloseControls(true, false);
mTestableLooper.processAllMessages();
@@ -542,10 +841,21 @@
public void testHandleCloseControls_DoesNotUpdateNotificationChannelIfUnspecified()
throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, false,
- IMPORTANCE_UNSPECIFIED, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_UNSPECIFIED,
+ true);
mNotificationInfo.handleCloseControls(true, false);
@@ -561,13 +871,22 @@
NotificationInfo.CheckSaveListener listener =
mock(NotificationInfo.CheckSaveListener.class);
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
- createMultipleChannelSet(10) /* numUniqueChannelsInRow */, mSbn,
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel /* notificationChannel */,
+ createMultipleChannelSet(10) /* numUniqueChannelsInRow */,
+ mSbn,
listener /* checkSaveListener */,
- null /* onSettingsClick */, null /* onAppSettingsClick */, true /* provisioned */,
- false /* isNonblockable */, true /* isForBlockingHelper */,
- IMPORTANCE_DEFAULT, true);
+ null /* onSettingsClick */,
+ null /* onAppSettingsClick */,
+ true /* provisioned */,
+ false /* isNonblockable */,
+ true /* isForBlockingHelper */,
+ IMPORTANCE_DEFAULT,
+ true);
NotificationGuts guts = spy(new NotificationGuts(mContext, null));
when(guts.getWindowToken()).thenReturn(mock(IBinder.class));
@@ -591,13 +910,21 @@
NotificationInfo.CheckSaveListener listener =
mock(NotificationInfo.CheckSaveListener.class);
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
- createMultipleChannelSet(10) /* numUniqueChannelsInRow */, mSbn,
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel /* notificationChannel */,
+ createMultipleChannelSet(10) /* numUniqueChannelsInRow */,
+ mSbn,
listener /* checkSaveListener */,
- null /* onSettingsClick */, null /* onAppSettingsClick */,
- false /* isNonblockable */, true /* isForBlockingHelper */,
- true, IMPORTANCE_DEFAULT, true);
+ null /* onSettingsClick */,
+ null /* onAppSettingsClick */,
+ false /* isNonblockable */,
+ true /* isForBlockingHelper */,
+ true, IMPORTANCE_DEFAULT,
+ true);
mNotificationInfo.handleCloseControls(true /* save */, false /* force */);
@@ -611,14 +938,22 @@
NotificationInfo.CheckSaveListener listener =
mock(NotificationInfo.CheckSaveListener.class);
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
- createMultipleChannelSet(10) /* numUniqueChannelsInRow */, mSbn,
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel /* notificationChannel */,
+ createMultipleChannelSet(10) /* numUniqueChannelsInRow */,
+ mSbn,
listener /* checkSaveListener */,
- null /* onSettingsClick */, null /* onAppSettingsClick */,
+ null /* onSettingsClick */,
+ null /* onAppSettingsClick */,
true /* provisioned */,
- false /* isNonblockable */, true /* isForBlockingHelper */,
- IMPORTANCE_DEFAULT, true);
+ false /* isNonblockable */,
+ true /* isForBlockingHelper */,
+ IMPORTANCE_DEFAULT,
+ true);
mNotificationInfo.findViewById(R.id.deliver_silently).performClick();
mTestableLooper.processAllMessages();
@@ -631,6 +966,7 @@
mNotificationInfo.bindNotification(
mMockPackageManager,
mMockINotificationManager,
+ mVisualStabilityManager,
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet /* numChannels */,
@@ -641,7 +977,8 @@
false /* isNonblockable */,
true /* isForBlockingHelper */,
true,
- IMPORTANCE_DEFAULT, true);
+ IMPORTANCE_DEFAULT,
+ true);
NotificationGuts guts = mock(NotificationGuts.class);
doCallRealMethod().when(guts).closeControls(anyInt(), anyInt(), anyBoolean(), anyBoolean());
mNotificationInfo.setGutsParent(guts);
@@ -658,6 +995,7 @@
mNotificationInfo.bindNotification(
mMockPackageManager,
mMockINotificationManager,
+ mVisualStabilityManager,
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet /* numChannels */,
@@ -688,10 +1026,21 @@
@Test
public void testKeepUpdatesNotificationChannel_blockingHelper() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, true,
- IMPORTANCE_LOW, false);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ true,
+ IMPORTANCE_LOW,
+ false);
mNotificationInfo.findViewById(R.id.keep_showing).performClick();
mNotificationInfo.handleCloseControls(true, false);
@@ -708,10 +1057,21 @@
@Test
public void testNoActionsUpdatesNotificationChannel_blockingHelper() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, true,
- IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ true,
+ IMPORTANCE_DEFAULT,
+ true);
mNotificationInfo.handleCloseControls(true, false);
@@ -727,10 +1087,21 @@
@Test
public void testSilenceCallsUpdateNotificationChannel() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, false,
- IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
mNotificationInfo.findViewById(R.id.silence).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -749,10 +1120,21 @@
@Test
public void testUnSilenceCallsUpdateNotificationChannel() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, false,
- IMPORTANCE_LOW, false);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_LOW,
+ false);
mNotificationInfo.findViewById(R.id.alert).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -772,10 +1154,21 @@
public void testSilenceCallsUpdateNotificationChannel_channelImportanceUnspecified()
throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, false,
- IMPORTANCE_UNSPECIFIED, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_UNSPECIFIED,
+ true);
mNotificationInfo.findViewById(R.id.silence).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -795,10 +1188,21 @@
public void testSilenceCallsUpdateNotificationChannel_channelImportanceMin()
throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_MIN);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, false,
- IMPORTANCE_MIN, false);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_MIN,
+ false);
assertEquals(mContext.getString(R.string.inline_done_button),
((TextView) mNotificationInfo.findViewById(R.id.done)).getText());
@@ -821,10 +1225,21 @@
public void testAlertCallsUpdateNotificationChannel_channelImportanceMin()
throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_MIN);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, false,
- IMPORTANCE_MIN, false);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_MIN,
+ false);
assertEquals(mContext.getString(R.string.inline_done_button),
((TextView) mNotificationInfo.findViewById(R.id.done)).getText());
@@ -844,13 +1259,50 @@
}
@Test
+ public void testAdjustImportanceTemporarilyAllowsReordering() throws Exception {
+ mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
+
+ mNotificationInfo.findViewById(R.id.silence).performClick();
+ mNotificationInfo.findViewById(R.id.done).performClick();
+ mNotificationInfo.handleCloseControls(true, false);
+
+ verify(mVisualStabilityManager).temporarilyAllowReordering();
+ }
+
+ @Test
public void testDoneText()
throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, false,
- IMPORTANCE_LOW, false);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_LOW,
+ false);
assertEquals(mContext.getString(R.string.inline_done_button),
((TextView) mNotificationInfo.findViewById(R.id.done)).getText());
@@ -866,10 +1318,21 @@
public void testUnSilenceCallsUpdateNotificationChannel_channelImportanceUnspecified()
throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, false,
- IMPORTANCE_LOW, false);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_LOW,
+ false);
mNotificationInfo.findViewById(R.id.alert).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -888,10 +1351,21 @@
@Test
public void testCloseControlsDoesNotUpdateIfSaveIsFalse() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, false,
- IMPORTANCE_LOW, false);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_LOW,
+ false);
mNotificationInfo.findViewById(R.id.alert).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -905,11 +1379,23 @@
@Test
public void testCloseControlsUpdatesWhenCheckSaveListenerUsesCallback() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn,
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
(Runnable saveImportance, StatusBarNotification sbn) -> {
saveImportance.run();
- }, null, null, true, false, IMPORTANCE_LOW, false
+ },
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_LOW,
+ false
);
mNotificationInfo.findViewById(R.id.alert).performClick();
@@ -928,11 +1414,23 @@
@Test
public void testCloseControls_withoutHittingApply() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn,
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
(Runnable saveImportance, StatusBarNotification sbn) -> {
saveImportance.run();
- }, null, null, true, false, IMPORTANCE_LOW, false
+ },
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_LOW,
+ false
);
mNotificationInfo.findViewById(R.id.alert).performClick();
@@ -944,11 +1442,23 @@
public void testWillBeRemovedReturnsFalse() throws Exception {
assertFalse(mNotificationInfo.willBeRemoved());
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn,
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
(Runnable saveImportance, StatusBarNotification sbn) -> {
saveImportance.run();
- }, null, null, true, false, IMPORTANCE_LOW, false
+ },
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_LOW,
+ false
);
assertFalse(mNotificationInfo.willBeRemoved());
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 5da1a19..39ce4a0 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -3649,6 +3649,13 @@
|| nai.networkMisc.acceptPartialConnectivity) {
return;
}
+
+ // Stop automatically reconnecting to this network in the future. Automatically connecting
+ // to a network that provides no or limited connectivity is not useful, because the user
+ // cannot use that network except through the notification shown by this method, and the
+ // notification is only shown if the network is explicitly selected by the user.
+ nai.asyncChannel.sendMessage(NetworkAgent.CMD_PREVENT_AUTOMATIC_RECONNECT);
+
// TODO: Evaluate if it's needed to wait 8 seconds for triggering notification when
// NetworkMonitor detects the network is partial connectivity. Need to change the design to
// popup the notification immediately when the network is partial connectivity.
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index e097d85..4e416a2 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -63,7 +63,7 @@
static final String TAG = "Watchdog";
/** Debug flag. */
- public static final boolean DEBUG = true; // STOPSHIP disable it (b/113252928)
+ public static final boolean DEBUG = false;
// Set this to true to use debug default values.
static final boolean DB = false;
@@ -570,7 +570,7 @@
continue;
} else if (waitState == WAITED_HALF) {
if (!waitedHalf) {
- if (DEBUG) Slog.d(TAG, "WAITED_HALF");
+ Slog.i(TAG, "WAITED_HALF");
// We've waited half the deadlock-detection interval. Pull a stack
// trace and wait another half.
ArrayList<Integer> pids = new ArrayList<Integer>();
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 376e9b5..e43c548 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -162,6 +162,7 @@
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* The implementation of the volume manager service.
@@ -265,6 +266,7 @@
private static final int MSG_SET_DEVICE_STREAM_VOLUME = 26;
private static final int MSG_OBSERVE_DEVICES_FOR_ALL_STREAMS = 27;
private static final int MSG_HDMI_VOLUME_CHECK = 28;
+ private static final int MSG_PLAYBACK_CONFIG_CHANGE = 29;
// start of messages handled under wakelock
// these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
// and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
@@ -1867,9 +1869,15 @@
// Check if volume update should be send to Hearing Aid
if ((device & AudioSystem.DEVICE_OUT_HEARING_AID) != 0) {
- Log.i(TAG, "adjustSreamVolume postSetHearingAidVolumeIndex index=" + newIndex
- + " stream=" + streamType);
- mDeviceBroker.postSetHearingAidVolumeIndex(newIndex, streamType);
+ // only modify the hearing aid attenuation when the stream to modify matches
+ // the one expected by the hearing aid
+ if (streamType == getHearingAidStreamType()) {
+ if (DEBUG_VOL) {
+ Log.d(TAG, "adjustSreamVolume postSetHearingAidVolumeIndex index="
+ + newIndex + " stream=" + streamType);
+ }
+ mDeviceBroker.postSetHearingAidVolumeIndex(newIndex, streamType);
+ }
}
// Check if volume update should be sent to Hdmi system audio.
@@ -2161,11 +2169,53 @@
return AudioSystem.STREAM_VOICE_CALL;
case AudioSystem.MODE_NORMAL:
default:
+ // other conditions will influence the stream type choice, read on...
break;
}
+ if (mVoiceActive.get()) {
+ return AudioSystem.STREAM_VOICE_CALL;
+ }
return AudioSystem.STREAM_MUSIC;
}
+ private AtomicBoolean mVoiceActive = new AtomicBoolean(false);
+
+ private final IPlaybackConfigDispatcher mVoiceActivityMonitor =
+ new IPlaybackConfigDispatcher.Stub() {
+ @Override
+ public void dispatchPlaybackConfigChange(List<AudioPlaybackConfiguration> configs,
+ boolean flush) {
+ sendMsg(mAudioHandler, MSG_PLAYBACK_CONFIG_CHANGE, SENDMSG_REPLACE,
+ 0 /*arg1 ignored*/, 0 /*arg2 ignored*/,
+ configs /*obj*/, 0 /*delay*/);
+ }
+ };
+
+ private void onPlaybackConfigChange(List<AudioPlaybackConfiguration> configs) {
+ boolean voiceActive = false;
+ for (AudioPlaybackConfiguration config : configs) {
+ final int usage = config.getAudioAttributes().getUsage();
+ if ((usage == AudioAttributes.USAGE_VOICE_COMMUNICATION
+ || usage == AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING)
+ && config.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
+ voiceActive = true;
+ break;
+ }
+ }
+ if (mVoiceActive.getAndSet(voiceActive) != voiceActive) {
+ updateHearingAidVolumeOnVoiceActivityUpdate();
+ }
+ }
+
+ private void updateHearingAidVolumeOnVoiceActivityUpdate() {
+ final int streamType = getHearingAidStreamType();
+ final int index = getStreamVolume(streamType);
+ sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_VOICE_ACTIVITY_HEARING_AID,
+ mVoiceActive.get(), streamType, index));
+ mDeviceBroker.postSetHearingAidVolumeIndex(index * 10, streamType);
+
+ }
+
/**
* Manage an audio mode change for audio devices that use an "absolute volume" model,
* i.e. the framework sends the full scale signal, and the actual volume for the use case
@@ -2200,10 +2250,8 @@
// handling of specific interfaces goes here:
if ((device & mAbsVolumeMultiModeCaseDevices) == AudioSystem.DEVICE_OUT_HEARING_AID) {
final int index = getStreamVolume(streamType);
- mModeLogger.log(new AudioEventLogger.StringEvent("setMode to "
- + AudioSystem.modeToString(newMode)
- + " causes setting HEARING_AID volume to idx:" + index
- + " stream:" + AudioSystem.streamToString(streamType)));
+ sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_MODE_CHANGE_HEARING_AID,
+ newMode, streamType, index));
mDeviceBroker.postSetHearingAidVolumeIndex(index * 10, streamType);
}
}
@@ -2269,7 +2317,8 @@
mDeviceBroker.postSetAvrcpAbsoluteVolumeIndex(index / 10);
}
- if ((device & AudioSystem.DEVICE_OUT_HEARING_AID) != 0) {
+ if ((device & AudioSystem.DEVICE_OUT_HEARING_AID) != 0
+ && streamType == getHearingAidStreamType()) {
Log.i(TAG, "setStreamVolume postSetHearingAidVolumeIndex index=" + index
+ " stream=" + streamType);
mDeviceBroker.postSetHearingAidVolumeIndex(index, streamType);
@@ -4345,6 +4394,11 @@
throw new IllegalArgumentException("Illegal BluetoothProfile state for device "
+ " (dis)connection, got " + state);
}
+ if (state == BluetoothProfile.STATE_CONNECTED) {
+ mPlaybackMonitor.registerPlaybackCallback(mVoiceActivityMonitor, true);
+ } else {
+ mPlaybackMonitor.unregisterPlaybackCallback(mVoiceActivityMonitor);
+ }
mDeviceBroker.postBluetoothHearingAidDeviceConnectionState(
device, state, suppressNoisyIntent, musicDevice, "AudioService");
}
@@ -4832,6 +4886,7 @@
pw.println((mIndexMin + 5) / 10);
pw.print(" Max: ");
pw.println((mIndexMax + 5) / 10);
+ pw.print(" streamVolume:"); pw.println(getStreamVolume(mStreamType));
pw.print(" Current: ");
for (int i = 0; i < mIndexMap.size(); i++) {
if (i > 0) {
@@ -5408,6 +5463,11 @@
case MSG_HDMI_VOLUME_CHECK:
onCheckVolumeCecOnHdmiConnection(msg.arg1, (String) msg.obj);
+ break;
+
+ case MSG_PLAYBACK_CONFIG_CHANGE:
+ onPlaybackConfigChange((List<AudioPlaybackConfiguration>) msg.obj);
+ break;
}
}
}
diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java
index d999217..fcd8701 100644
--- a/services/core/java/com/android/server/audio/AudioServiceEvents.java
+++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java
@@ -95,6 +95,8 @@
static final int VOL_SET_HEARING_AID_VOL = 3;
static final int VOL_SET_AVRCP_VOL = 4;
static final int VOL_ADJUST_VOL_UID = 5;
+ static final int VOL_VOICE_ACTIVITY_HEARING_AID = 6;
+ static final int VOL_MODE_CHANGE_HEARING_AID = 7;
final int mOp;
final int mStream;
@@ -102,6 +104,10 @@
final int mVal2;
final String mCaller;
+ /** used for VOL_ADJUST_VOL_UID,
+ * VOL_ADJUST_SUGG_VOL,
+ * VOL_ADJUST_STREAM_VOL,
+ * VOL_SET_STREAM_VOL */
VolumeEvent(int op, int stream, int val1, int val2, String caller) {
mOp = op;
mStream = stream;
@@ -110,24 +116,46 @@
mCaller = caller;
}
+ /** used for VOL_SET_HEARING_AID_VOL*/
VolumeEvent(int op, int index, int gainDb) {
mOp = op;
mVal1 = index;
mVal2 = gainDb;
- //unused
+ // unused
mStream = -1;
mCaller = null;
}
+ /** used for VOL_SET_AVRCP_VOL */
VolumeEvent(int op, int index) {
mOp = op;
mVal1 = index;
- //unused
+ // unused
mVal2 = 0;
mStream = -1;
mCaller = null;
}
+ /** used for VOL_VOICE_ACTIVITY_HEARING_AID */
+ VolumeEvent(int op, boolean voiceActive, int stream, int index) {
+ mOp = op;
+ mStream = stream;
+ mVal1 = index;
+ mVal2 = voiceActive ? 1 : 0;
+ // unused
+ mCaller = null;
+ }
+
+ /** used for VOL_MODE_CHANGE_HEARING_AID */
+ VolumeEvent(int op, int mode, int stream, int index) {
+ mOp = op;
+ mStream = stream;
+ mVal1 = index;
+ mVal2 = mode;
+ // unused
+ mCaller = null;
+ }
+
@Override
public String eventToString() {
switch (mOp) {
@@ -168,7 +196,19 @@
.append(" flags:0x").append(Integer.toHexString(mVal2))
.append(") from ").append(mCaller)
.toString();
- default: return new StringBuilder("FIXME invalid op:").append(mOp).toString();
+ case VOL_VOICE_ACTIVITY_HEARING_AID:
+ return new StringBuilder("Voice activity change (")
+ .append(mVal2 == 1 ? "active" : "inactive")
+ .append(") causes setting HEARING_AID volume to idx:").append(mVal1)
+ .append(" stream:").append(AudioSystem.streamToString(mStream))
+ .toString();
+ case VOL_MODE_CHANGE_HEARING_AID:
+ return new StringBuilder("setMode(")
+ .append(AudioSystem.modeToString(mVal2))
+ .append(") causes setting HEARING_AID volume to idx:").append(mVal1)
+ .append(" stream:").append(AudioSystem.streamToString(mStream))
+ .toString();
+ default: return new StringBuilder("FIXME invalid op:").append(mOp).toString();
}
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 1c775bd..5958715 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2578,9 +2578,7 @@
mIsPreNUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N;
mIsPreNMR1Upgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N_MR1;
- mIsPreQUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.Q
- // STOPSHIP: Remove next line when API level for Q is defined.
- && Build.VERSION.SDK_INT > Build.VERSION_CODES.P;
+ mIsPreQUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.Q;
int preUpgradeSdkVersion = ver.sdkVersion;
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index beb7268..4edd9ef 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -97,6 +97,7 @@
import com.android.server.pm.UserManagerService;
import com.android.server.pm.permission.PermissionManagerServiceInternal.PermissionCallback;
import com.android.server.pm.permission.PermissionsState.PermissionState;
+import com.android.server.policy.SoftRestrictedPermissionPolicy;
import libcore.util.EmptyArray;
@@ -2121,11 +2122,18 @@
if (bp.isHardRestricted()
&& (flags & PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) == 0) {
- Log.e(TAG, "Cannot grant restricted non-exempt permission "
+ Log.e(TAG, "Cannot grant hard restricted non-exempt permission "
+ permName + " for package " + packageName);
return;
}
+ if (bp.isSoftRestricted() && !SoftRestrictedPermissionPolicy.forPermission(mContext,
+ pkg.applicationInfo, permName).canBeGranted()) {
+ Log.e(TAG, "Cannot grant soft restricted permission " + permName + " for package "
+ + packageName);
+ return;
+ }
+
if (bp.isDevelopment()) {
// Development permissions must be handled specially, since they are not
// normal runtime permissions. For now they apply to all users.
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index a799cd9..1d01a84 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -16,10 +16,14 @@
package com.android.server.policy;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_DEFAULT;
+import static android.app.AppOpsManager.MODE_ERRORED;
+import static android.app.AppOpsManager.MODE_IGNORED;
+import static android.app.AppOpsManager.OP_NONE;
import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
-import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -426,20 +430,34 @@
mOpsToAllowIfDefault.add(new OpToUnrestrict(uid, pkg.packageName, opCode));
}
} else if (permissionInfo.isSoftRestricted()) {
- // Storage uses a special app op to decide the mount state and
- // supports soft restriction where the restricted state allows
- // the permission but only for accessing the medial collections.
- if (Manifest.permission.READ_EXTERNAL_STORAGE.equals(permission)
- || Manifest.permission.WRITE_EXTERNAL_STORAGE.equals(permission)) {
- if (applyRestriction) {
- mOpsToDefault.add(new OpToRestrict(uid,
- AppOpsManager.OP_LEGACY_STORAGE));
- } else if (pkg.applicationInfo.hasRequestedLegacyExternalStorage()) {
- mOpsToAllow.add(new OpToUnrestrict(uid, pkg.packageName,
- AppOpsManager.OP_LEGACY_STORAGE));
- } else {
- mOpsToIgnoreIfDefault.add(new OpToUnrestrict(uid, pkg.packageName,
- AppOpsManager.OP_LEGACY_STORAGE));
+ final SoftRestrictedPermissionPolicy policy =
+ SoftRestrictedPermissionPolicy.forPermission(mContext, pkg.applicationInfo,
+ permission);
+
+ final int op = policy.getAppOp();
+ if (op != OP_NONE) {
+ switch (policy.getAppOpMode()) {
+ case MODE_DEFAULT:
+ mOpsToDefault.add(new OpToRestrict(uid, op));
+ break;
+ case MODE_ALLOWED:
+ if (policy.shouldSetAppOpIfNotDefault()) {
+ mOpsToAllow.add(new OpToUnrestrict(uid, pkg.packageName, op));
+ } else {
+ mOpsToAllowIfDefault.add(new OpToUnrestrict(uid, pkg.packageName,
+ op));
+ }
+ break;
+ case MODE_IGNORED:
+ if (policy.shouldSetAppOpIfNotDefault()) {
+ Slog.wtf(LOG_TAG, "Always ignoring appops is not implemented");
+ } else {
+ mOpsToIgnoreIfDefault.add(new OpToUnrestrict(uid, pkg.packageName,
+ op));
+ }
+ break;
+ case MODE_ERRORED:
+ Slog.wtf(LOG_TAG, "Setting appop to errored is not implemented");
}
}
}
@@ -483,7 +501,7 @@
for (String permission : pkg.requestedPermissions) {
final int opCode = AppOpsManager.permissionToOpCode(permission);
- if (opCode == AppOpsManager.OP_NONE) {
+ if (opCode == OP_NONE) {
continue;
}
@@ -515,13 +533,13 @@
@NonNull String packageName) {
final int currentMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager
.opToPublicName(opCode), uid, packageName);
- if (currentMode == AppOpsManager.MODE_DEFAULT) {
+ if (currentMode == MODE_DEFAULT) {
mAppOpsManager.setUidMode(opCode, uid, mode);
}
}
private void setUidModeDefault(int opCode, int uid) {
- mAppOpsManager.setUidMode(opCode, uid, AppOpsManager.MODE_DEFAULT);
+ mAppOpsManager.setUidMode(opCode, uid, MODE_DEFAULT);
}
private class OpToRestrict {
diff --git a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
new file mode 100644
index 0000000..e19b708
--- /dev/null
+++ b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
@@ -0,0 +1,165 @@
+/*
+ * 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.policy;
+
+import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
+import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_DEFAULT;
+import static android.app.AppOpsManager.MODE_IGNORED;
+import static android.app.AppOpsManager.OP_LEGACY_STORAGE;
+import static android.app.AppOpsManager.OP_NONE;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
+
+import android.annotation.NonNull;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.os.Build;
+import android.util.Log;
+
+/**
+ * The behavior of soft restricted permissions is different for each permission. This class collects
+ * the policies in one place.
+ *
+ * This is the twin of
+ * {@link com.android.packageinstaller.permission.utils.SoftRestrictedPermissionPolicy}
+ */
+public abstract class SoftRestrictedPermissionPolicy {
+ private static final String LOG_TAG = SoftRestrictedPermissionPolicy.class.getSimpleName();
+
+ private static final int FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT =
+ FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT
+ | FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT
+ | FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
+
+ private static final SoftRestrictedPermissionPolicy DUMMY_POLICY =
+ new SoftRestrictedPermissionPolicy() {
+ @Override
+ public int getAppOp() {
+ return OP_NONE;
+ }
+
+ @Override
+ public int getAppOpMode() {
+ return MODE_DEFAULT;
+ }
+
+ @Override
+ public boolean shouldSetAppOpIfNotDefault() {
+ return false;
+ }
+
+ @Override
+ public boolean canBeGranted() {
+ return true;
+ }
+ };
+
+ /**
+ * Get the policy for a soft restricted permission.
+ *
+ * @param context A context to use
+ * @param appInfo The application the permission belongs to
+ * @param permission The name of the permission
+ *
+ * @return The policy for this permission
+ */
+ public static @NonNull SoftRestrictedPermissionPolicy forPermission(@NonNull Context context,
+ @NonNull ApplicationInfo appInfo, @NonNull String permission) {
+ switch (permission) {
+ // Storage uses a special app op to decide the mount state and supports soft restriction
+ // where the restricted state allows the permission but only for accessing the medial
+ // collections.
+ case READ_EXTERNAL_STORAGE:
+ case WRITE_EXTERNAL_STORAGE: {
+ int flags = context.getPackageManager().getPermissionFlags(
+ permission, appInfo.packageName, context.getUser());
+ boolean applyRestriction = (flags & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
+ boolean isWhiteListed = (flags & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
+ boolean hasRequestedLegacyExternalStorage =
+ appInfo.hasRequestedLegacyExternalStorage();
+ int targetSDK = appInfo.targetSdkVersion;
+
+ return new SoftRestrictedPermissionPolicy() {
+ @Override
+ public int getAppOp() {
+ return OP_LEGACY_STORAGE;
+ }
+
+ @Override
+ public int getAppOpMode() {
+ if (applyRestriction) {
+ return MODE_DEFAULT;
+ } else if (hasRequestedLegacyExternalStorage) {
+ return MODE_ALLOWED;
+ } else {
+ return MODE_IGNORED;
+ }
+ }
+
+ @Override
+ public boolean shouldSetAppOpIfNotDefault() {
+ // Do not switch from allowed -> ignored as this would mean to retroactively
+ // turn on isolated storage. This will make the app loose all its files.
+ return getAppOpMode() != MODE_IGNORED;
+ }
+
+ @Override
+ public boolean canBeGranted() {
+ if (isWhiteListed || targetSDK >= Build.VERSION_CODES.Q) {
+ return true;
+ } else {
+ Log.w(LOG_TAG, permission + " for " + appInfo.packageName
+ + " is not whitelisted and targetSDK " + targetSDK + "<"
+ + Build.VERSION_CODES.Q);
+
+ return false;
+ }
+ }
+ };
+ }
+ default:
+ return DUMMY_POLICY;
+ }
+ }
+
+ /**
+ * @return An app op to be changed based on the state of the permission or
+ * {@link AppOpsManager#OP_NONE} if not app-op should be set.
+ */
+ public abstract int getAppOp();
+
+ /**
+ * @return The mode the {@link #getAppOp() app op} should be in.
+ */
+ public abstract @AppOpsManager.Mode int getAppOpMode();
+
+ /**
+ * @return If the {@link #getAppOp() app op} should be set even if the app-op is currently not
+ * {@link AppOpsManager#MODE_DEFAULT}.
+ */
+ public abstract boolean shouldSetAppOpIfNotDefault();
+
+ /**
+ * @return If the permission can be granted
+ */
+ public abstract boolean canBeGranted();
+}
diff --git a/services/core/java/com/android/server/policy/TEST_MAPPING b/services/core/java/com/android/server/policy/TEST_MAPPING
index 437ef73..02b0e21 100644
--- a/services/core/java/com/android/server/policy/TEST_MAPPING
+++ b/services/core/java/com/android/server/policy/TEST_MAPPING
@@ -27,6 +27,14 @@
"exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
+ },
+ {
+ "name": "CtsPermission2TestCases",
+ "options": [
+ {
+ "include-filter": "android.permission2.cts.RestrictedPermissionsTest"
+ }
+ ]
}
],
"postsubmit": [
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerInternal.java b/services/core/java/com/android/server/uri/UriGrantsManagerInternal.java
index 2f50fcb..94a1baa 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerInternal.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerInternal.java
@@ -43,14 +43,14 @@
int callingUid, String targetPkg, Uri uri, int modeFlags, int userId);
NeededUriGrants checkGrantUriPermissionFromIntent(int callingUid,
String targetPkg, Intent intent, int mode, NeededUriGrants needed, int targetUserId);
+ NeededUriGrants checkGrantUriPermissionFromIntent(int callingUid,
+ Intent intent, String targetPkg, int targetUserId);
/**
* Grant Uri permissions from one app to another. This method only extends
* permission grants if {@code callingUid} has permission to them.
*/
void grantUriPermissionFromIntent(int callingUid,
String targetPkg, Intent intent, int targetUserId);
- void grantUriPermissionFromIntent(int callingUid,
- String targetPkg, Intent intent, UriPermissionOwner owner, int targetUserId);
void grantUriPermissionUncheckedFromIntent(
NeededUriGrants needed, UriPermissionOwner owner);
IBinder newUriPermissionOwner(String name);
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
index 8b332d2..04f7c7e 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -1362,20 +1362,21 @@
}
@Override
- public void grantUriPermissionFromIntent(int callingUid, String targetPkg, Intent intent,
- int targetUserId) {
+ public NeededUriGrants checkGrantUriPermissionFromIntent(int callingUid, Intent intent,
+ String targetPkg, int targetUserId) {
synchronized (mLock) {
- UriGrantsManagerService.this.grantUriPermissionFromIntent(
- callingUid, targetPkg, intent, null, targetUserId);
+ final int mode = (intent != null) ? intent.getFlags() : 0;
+ return UriGrantsManagerService.this.checkGrantUriPermissionFromIntent(
+ callingUid, targetPkg, intent, mode, null, targetUserId);
}
}
@Override
public void grantUriPermissionFromIntent(int callingUid, String targetPkg, Intent intent,
- UriPermissionOwner owner, int targetUserId) {
+ int targetUserId) {
synchronized (mLock) {
UriGrantsManagerService.this.grantUriPermissionFromIntent(
- callingUid, targetPkg, intent, owner, targetUserId);
+ callingUid, targetPkg, intent, null, targetUserId);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 1344727..7c4b571 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -215,6 +215,7 @@
import com.android.server.AttributeCache.Entry;
import com.android.server.am.AppTimeTracker;
import com.android.server.am.PendingIntentRecord;
+import com.android.server.uri.NeededUriGrants;
import com.android.server.uri.UriPermissionOwner;
import com.android.server.wm.ActivityMetricsLogger.WindowingModeTransitionInfoSnapshot;
import com.android.server.wm.ActivityStack.ActivityState;
@@ -1599,10 +1600,11 @@
* Deliver a new Intent to an existing activity, so that its onNewIntent()
* method will be called at the proper time.
*/
- final void deliverNewIntentLocked(int callingUid, Intent intent, String referrer) {
+ final void deliverNewIntentLocked(int callingUid, Intent intent, NeededUriGrants intentGrants,
+ String referrer) {
// The activity now gets access to the data associated with this Intent.
- mAtmService.mUgmInternal.grantUriPermissionFromIntent(callingUid, packageName,
- intent, getUriPermissionsLocked(), mUserId);
+ mAtmService.mUgmInternal.grantUriPermissionUncheckedFromIntent(intentGrants,
+ getUriPermissionsLocked());
final ReferrerIntent rintent = new ReferrerIntent(intent, referrer);
boolean unsent = true;
final boolean isTopActivityWhileSleeping = isTopRunningActivity() && isSleeping();
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 3d59e66..1ad5e7c 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -162,6 +162,7 @@
import com.android.server.am.AppTimeTracker;
import com.android.server.am.EventLogTags;
import com.android.server.am.PendingIntentRecord;
+import com.android.server.uri.NeededUriGrants;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -2780,7 +2781,7 @@
if (DEBUG_STATES) Slog.d(TAG_STATES,
"no-history finish of " + mLastNoHistoryActivity + " on new resume");
requestFinishActivityLocked(mLastNoHistoryActivity.appToken, Activity.RESULT_CANCELED,
- null, "resume-no-history", false);
+ null, null, "resume-no-history", false);
mLastNoHistoryActivity = null;
}
@@ -3014,7 +3015,7 @@
// If any exception gets thrown, toss away this
// activity and try the next one.
Slog.w(TAG, "Exception thrown during resume of " + next, e);
- requestFinishActivityLocked(next.appToken, Activity.RESULT_CANCELED, null,
+ requestFinishActivityLocked(next.appToken, Activity.RESULT_CANCELED, null, null,
"resume-exception", true);
return true;
}
@@ -3422,7 +3423,7 @@
if (DEBUG_TASKS) Slog.w(TAG_TASKS,
"resetTaskIntendedTask: calling finishActivity on " + p);
if (finishActivityLocked(
- p, Activity.RESULT_CANCELED, null, "reset-task", false)) {
+ p, Activity.RESULT_CANCELED, null, null, "reset-task", false)) {
end--;
srcPos--;
}
@@ -3501,7 +3502,7 @@
continue;
}
finishActivityLocked(
- p, Activity.RESULT_CANCELED, null, "move-affinity", false);
+ p, Activity.RESULT_CANCELED, null, null, "move-affinity", false);
}
} else {
if (taskInsertionPoint < 0) {
@@ -3535,8 +3536,8 @@
if (targetNdx > 0) {
ActivityRecord p = taskActivities.get(targetNdx - 1);
if (p.intent.getComponent().equals(target.intent.getComponent())) {
- finishActivityLocked(p, Activity.RESULT_CANCELED, null, "replace",
- false);
+ finishActivityLocked(p, Activity.RESULT_CANCELED, null, null,
+ "replace", false);
}
}
}
@@ -3596,22 +3597,21 @@
return taskTop;
}
- void sendActivityResultLocked(int callingUid, ActivityRecord r,
- String resultWho, int requestCode, int resultCode, Intent data) {
-
+ void sendActivityResultLocked(int callingUid, ActivityRecord r, String resultWho,
+ int requestCode, int resultCode, Intent resultData, NeededUriGrants resultGrants) {
if (callingUid > 0) {
- mService.mUgmInternal.grantUriPermissionFromIntent(callingUid, r.packageName,
- data, r.getUriPermissionsLocked(), r.mUserId);
+ mService.mUgmInternal.grantUriPermissionUncheckedFromIntent(resultGrants,
+ r.getUriPermissionsLocked());
}
if (DEBUG_RESULTS) Slog.v(TAG, "Send activity result to " + r
+ " : who=" + resultWho + " req=" + requestCode
- + " res=" + resultCode + " data=" + data);
+ + " res=" + resultCode + " data=" + resultData);
if (mResumedActivity == r && r.attachedToProcess()) {
try {
ArrayList<ResultInfo> list = new ArrayList<ResultInfo>();
list.add(new ResultInfo(resultWho, requestCode,
- resultCode, data));
+ resultCode, resultData));
mService.getLifecycleManager().scheduleTransaction(r.app.getThread(), r.appToken,
ActivityResultItem.obtain(list));
return;
@@ -3620,7 +3620,7 @@
}
}
- r.addResultLocked(null, resultWho, requestCode, resultCode, data);
+ r.addResultLocked(null, resultWho, requestCode, resultCode, resultData);
}
/** Returns true if the task is one of the task finishing on-top of the top running task. */
@@ -3727,8 +3727,8 @@
if (!r.finishing) {
if (!shouldSleepActivities()) {
if (DEBUG_STATES) Slog.d(TAG_STATES, "no-history finish of " + r);
- if (requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null,
- "stop-no-history", false)) {
+ if (requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED,
+ null, null, "stop-no-history", false)) {
// If {@link requestFinishActivityLocked} returns {@code true},
// {@link adjustFocusedActivityStack} would have been already called.
r.resumeKeyDispatchingLocked();
@@ -3784,7 +3784,7 @@
* some reason it is being left as-is.
*/
final boolean requestFinishActivityLocked(IBinder token, int resultCode,
- Intent resultData, String reason, boolean oomAdj) {
+ Intent resultData, NeededUriGrants resultGrants, String reason, boolean oomAdj) {
ActivityRecord r = isInStackLocked(token);
if (DEBUG_RESULTS || DEBUG_STATES) Slog.v(TAG_STATES,
"Finishing activity token=" + token + " r="
@@ -3794,7 +3794,7 @@
return false;
}
- finishActivityLocked(r, resultCode, resultData, reason, oomAdj);
+ finishActivityLocked(r, resultCode, resultData, resultGrants, reason, oomAdj);
return true;
}
@@ -3806,8 +3806,8 @@
if (r.resultTo == self && r.requestCode == requestCode) {
if ((r.resultWho == null && resultWho == null) ||
(r.resultWho != null && r.resultWho.equals(resultWho))) {
- finishActivityLocked(r, Activity.RESULT_CANCELED, null, "request-sub",
- false);
+ finishActivityLocked(r, Activity.RESULT_CANCELED, null, null,
+ "request-sub", false);
}
}
}
@@ -3837,7 +3837,7 @@
int activityNdx = task.mActivities.indexOf(r);
getDisplay().mDisplayContent.prepareAppTransition(
TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
- finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false);
+ finishActivityLocked(r, Activity.RESULT_CANCELED, null, null, reason, false);
finishedTask = task;
// Also terminate any activities below it that aren't yet
// stopped, to avoid a situation where one will get
@@ -3858,7 +3858,7 @@
if (!r.isActivityTypeHome() || mService.mHomeProcess != r.app) {
Slog.w(TAG, " Force finishing activity "
+ r.intent.getComponent().flattenToShortString());
- finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false);
+ finishActivityLocked(r, Activity.RESULT_CANCELED, null, null, reason, false);
}
}
}
@@ -3874,8 +3874,8 @@
for (int activityNdx = tr.mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
ActivityRecord r = tr.mActivities.get(activityNdx);
if (!r.finishing) {
- finishActivityLocked(r, Activity.RESULT_CANCELED, null, "finish-voice",
- false);
+ finishActivityLocked(r, Activity.RESULT_CANCELED, null, null,
+ "finish-voice", false);
didOne = true;
}
}
@@ -3911,12 +3911,14 @@
if (!Objects.equals(cur.taskAffinity, r.taskAffinity)) {
break;
}
- finishActivityLocked(cur, Activity.RESULT_CANCELED, null, "request-affinity", true);
+ finishActivityLocked(cur, Activity.RESULT_CANCELED, null, null,
+ "request-affinity", true);
}
return true;
}
- private void finishActivityResultsLocked(ActivityRecord r, int resultCode, Intent resultData) {
+ private void finishActivityResultsLocked(ActivityRecord r, int resultCode, Intent resultData,
+ NeededUriGrants resultGrants) {
// send the result
ActivityRecord resultTo = r.resultTo;
if (resultTo != null) {
@@ -3929,9 +3931,8 @@
}
}
if (r.info.applicationInfo.uid > 0) {
- mService.mUgmInternal.grantUriPermissionFromIntent(r.info.applicationInfo.uid,
- resultTo.packageName, resultData,
- resultTo.getUriPermissionsLocked(), resultTo.mUserId);
+ mService.mUgmInternal.grantUriPermissionUncheckedFromIntent(resultGrants,
+ resultTo.getUriPermissionsLocked());
}
resultTo.addResultLocked(r, r.resultWho, r.requestCode, resultCode, resultData);
r.resultTo = null;
@@ -3947,12 +3948,10 @@
r.icicle = null;
}
- /**
- * See {@link #finishActivityLocked(ActivityRecord, int, Intent, String, boolean, boolean)}
- */
final boolean finishActivityLocked(ActivityRecord r, int resultCode, Intent resultData,
- String reason, boolean oomAdj) {
- return finishActivityLocked(r, resultCode, resultData, reason, oomAdj, !PAUSE_IMMEDIATELY);
+ NeededUriGrants resultGrants, String reason, boolean oomAdj) {
+ return finishActivityLocked(r, resultCode, resultData, resultGrants, reason, oomAdj,
+ !PAUSE_IMMEDIATELY);
}
/**
@@ -3960,7 +3959,7 @@
* list, or false if it is still in the list and will be removed later.
*/
final boolean finishActivityLocked(ActivityRecord r, int resultCode, Intent resultData,
- String reason, boolean oomAdj, boolean pauseImmediately) {
+ NeededUriGrants resultGrants, String reason, boolean oomAdj, boolean pauseImmediately) {
if (r.finishing) {
Slog.w(TAG, "Duplicate finish request for " + r);
return false;
@@ -3990,7 +3989,7 @@
adjustFocusedActivityStack(r, "finishActivity");
- finishActivityResultsLocked(r, resultCode, resultData);
+ finishActivityResultsLocked(r, resultCode, resultData, resultGrants);
final boolean endTask = index <= 0 && !task.isClearingToReuseTask();
final int transit = endTask ? TRANSIT_TASK_CLOSE : TRANSIT_ACTIVITY_CLOSE;
@@ -4223,8 +4222,9 @@
return false;
}
- final boolean navigateUpToLocked(ActivityRecord srec, Intent destIntent, int resultCode,
- Intent resultData) {
+ final boolean navigateUpToLocked(ActivityRecord srec, Intent destIntent,
+ NeededUriGrants destGrants, int resultCode, Intent resultData,
+ NeededUriGrants resultGrants) {
final TaskRecord task = srec.getTaskRecord();
final ArrayList<ActivityRecord> activities = task.mActivities;
final int start = activities.indexOf(srec);
@@ -4271,7 +4271,8 @@
final long origId = Binder.clearCallingIdentity();
for (int i = start; i > finishTo; i--) {
ActivityRecord r = activities.get(i);
- requestFinishActivityLocked(r.appToken, resultCode, resultData, "navigate-up", true);
+ requestFinishActivityLocked(r.appToken, resultCode, resultData, resultGrants,
+ "navigate-up", true);
// Only return the supplied result for the first activity finished
resultCode = Activity.RESULT_CANCELED;
resultData = null;
@@ -4285,7 +4286,7 @@
parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TOP ||
(destIntentFlags & Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
parent.deliverNewIntentLocked(srec.info.applicationInfo.uid, destIntent,
- srec.packageName);
+ destGrants, srec.packageName);
} else {
try {
ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo(
@@ -4309,7 +4310,7 @@
foundParentInTask = false;
}
requestFinishActivityLocked(parent.appToken, resultCode,
- resultData, "navigate-top", true);
+ resultData, resultGrants, "navigate-top", true);
}
}
Binder.restoreCallingIdentity(origId);
@@ -4394,7 +4395,7 @@
}
private void removeActivityFromHistoryLocked(ActivityRecord r, String reason) {
- finishActivityResultsLocked(r, Activity.RESULT_CANCELED, null);
+ finishActivityResultsLocked(r, Activity.RESULT_CANCELED, null, null);
r.makeFinishingLocked();
if (DEBUG_ADD_REMOVE) Slog.i(TAG_ADD_REMOVE,
"Removing activity " + r + " from stack callers=" + Debug.getCallers(5));
@@ -5126,7 +5127,8 @@
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
final ActivityRecord r = activities.get(activityNdx);
if ((r.info.flags&ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS) != 0) {
- finishActivityLocked(r, Activity.RESULT_CANCELED, null, "close-sys", true);
+ finishActivityLocked(r, Activity.RESULT_CANCELED, null, null,
+ "close-sys", true);
}
}
}
@@ -5170,8 +5172,8 @@
didSomething = true;
Slog.i(TAG, " Force finishing activity " + r);
lastTask = r.getTaskRecord();
- finishActivityLocked(r, Activity.RESULT_CANCELED, null, "force-stop",
- true);
+ finishActivityLocked(r, Activity.RESULT_CANCELED, null, null,
+ "force-stop", true);
}
}
}
@@ -5225,8 +5227,8 @@
final ArrayList<ActivityRecord> activities = mTaskHistory.get(top).mActivities;
int activityTop = activities.size() - 1;
if (activityTop >= 0) {
- finishActivityLocked(activities.get(activityTop), Activity.RESULT_CANCELED, null,
- "unhandled-back", true);
+ finishActivityLocked(activities.get(activityTop), Activity.RESULT_CANCELED,
+ null, null, "unhandled-back", true);
}
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index c992a69..8d2a92a 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -140,6 +140,7 @@
import com.android.server.am.ActivityManagerService;
import com.android.server.am.EventLogTags;
import com.android.server.am.UserState;
+import com.android.server.uri.NeededUriGrants;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -403,14 +404,17 @@
final int startFlags;
final ActivityStack stack;
final WindowProcessController callerApp;
+ final NeededUriGrants neededGrants;
- PendingActivityLaunch(ActivityRecord _r, ActivityRecord _sourceRecord,
- int _startFlags, ActivityStack _stack, WindowProcessController app) {
- r = _r;
- sourceRecord = _sourceRecord;
- startFlags = _startFlags;
- stack = _stack;
- callerApp = app;
+ PendingActivityLaunch(ActivityRecord r, ActivityRecord sourceRecord,
+ int startFlags, ActivityStack stack, WindowProcessController callerApp,
+ NeededUriGrants neededGrants) {
+ this.r = r;
+ this.sourceRecord = sourceRecord;
+ this.startFlags = startFlags;
+ this.stack = stack;
+ this.callerApp = callerApp;
+ this.neededGrants = neededGrants;
}
void sendErrorResult(String message) {
@@ -874,8 +878,8 @@
Slog.e(TAG, "Second failure launching "
+ r.intent.getComponent().flattenToShortString() + ", giving up", e);
proc.appDied();
- stack.requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null,
- "2nd-crash", false);
+ stack.requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED,
+ null, null, "2nd-crash", false);
return false;
}
@@ -1020,7 +1024,7 @@
if (resultRecord != null) {
resultStack.sendActivityResultLocked(-1,
resultRecord, resultWho, requestCode,
- Activity.RESULT_CANCELED, null);
+ Activity.RESULT_CANCELED, null, null);
}
final String msg;
if (actionRestriction == ACTIVITY_RESTRICTION_PERMISSION) {
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 919141c..738d143 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -462,7 +462,7 @@
"pendingActivityLaunch");
try {
starter.startResolvedActivity(pal.r, pal.sourceRecord, null, null, pal.startFlags,
- resume, pal.r.pendingOptions, null);
+ resume, pal.r.pendingOptions, null, pal.neededGrants);
} catch (Exception e) {
Slog.e(TAG, "Exception during pending activity launch pal=" + pal, e);
pal.sendErrorResult(e.getMessage());
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 1718ac6..fb18569 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -127,6 +127,7 @@
import com.android.server.am.EventLogTags;
import com.android.server.am.PendingIntentRecord;
import com.android.server.pm.InstantAppResolver;
+import com.android.server.uri.NeededUriGrants;
import com.android.server.wm.ActivityStackSupervisor.PendingActivityLaunch;
import com.android.server.wm.LaunchParamsController.LaunchParams;
@@ -549,7 +550,8 @@
*/
int startResolvedActivity(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
- int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask) {
+ int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
+ NeededUriGrants neededGrants) {
try {
mSupervisor.getActivityMetricsLogger().notifyActivityLaunching(r.intent);
mLastStartReason = "startResolvedActivity";
@@ -557,7 +559,7 @@
mLastStartActivityRecord[0] = r;
mLastStartActivityResult = startActivity(r, sourceRecord, voiceSession, voiceInteractor,
startFlags, doResume, options, inTask, mLastStartActivityRecord,
- false /* restrictedBgActivity */);
+ false /* restrictedBgActivity */, neededGrants);
mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(mLastStartActivityResult,
mLastStartActivityRecord[0]);
return mLastStartActivityResult;
@@ -576,6 +578,33 @@
boolean allowPendingRemoteAnimationRegistryLookup,
PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {
+ // Carefully collect grants without holding lock
+ NeededUriGrants neededGrants = null;
+ if (aInfo != null) {
+ neededGrants = mService.mUgmInternal.checkGrantUriPermissionFromIntent(
+ resolveCallingUid(mRequest.caller), intent, aInfo.applicationInfo.packageName,
+ UserHandle.getUserId(aInfo.applicationInfo.uid));
+ }
+
+ return startActivity(caller, intent, ephemeralIntent, resolvedType, aInfo, rInfo,
+ voiceSession, voiceInteractor, resultTo, resultWho, requestCode, callingPid,
+ callingUid, callingPackage, realCallingPid, realCallingUid, startFlags, options,
+ ignoreTargetSecurity, componentSpecified, outActivity, inTask, reason,
+ allowPendingRemoteAnimationRegistryLookup, originatingPendingIntent,
+ allowBackgroundActivityStart, neededGrants);
+ }
+
+ private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
+ String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
+ IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
+ IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
+ String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
+ SafeActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,
+ ActivityRecord[] outActivity, TaskRecord inTask, String reason,
+ boolean allowPendingRemoteAnimationRegistryLookup,
+ PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart,
+ NeededUriGrants neededGrants) {
+
if (TextUtils.isEmpty(reason)) {
throw new IllegalArgumentException("Need to specify a reason.");
}
@@ -588,7 +617,7 @@
callingPid, callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
options, ignoreTargetSecurity, componentSpecified, mLastStartActivityRecord,
inTask, allowPendingRemoteAnimationRegistryLookup, originatingPendingIntent,
- allowBackgroundActivityStart);
+ allowBackgroundActivityStart, neededGrants);
if (outActivity != null) {
// mLastStartActivityRecord[0] is set in the call to startActivity above.
@@ -619,7 +648,8 @@
SafeActivityOptions options,
boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity,
TaskRecord inTask, boolean allowPendingRemoteAnimationRegistryLookup,
- PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {
+ PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart,
+ NeededUriGrants neededGrants) {
mSupervisor.getActivityMetricsLogger().notifyActivityLaunching(intent);
int err = ActivityManager.START_SUCCESS;
// Pull the optional Ephemeral Installer-only bundle out of the options early.
@@ -754,7 +784,7 @@
if (err != START_SUCCESS) {
if (resultRecord != null) {
resultStack.sendActivityResultLocked(
- -1, resultRecord, resultWho, requestCode, RESULT_CANCELED, null);
+ -1, resultRecord, resultWho, requestCode, RESULT_CANCELED, null, null);
}
SafeActivityOptions.abort(options);
return err;
@@ -814,12 +844,16 @@
callingPid = mInterceptor.mCallingPid;
callingUid = mInterceptor.mCallingUid;
checkedOptions = mInterceptor.mActivityOptions;
+
+ // The interception target shouldn't get any permission grants
+ // intended for the original destination
+ neededGrants = null;
}
if (abort) {
if (resultRecord != null) {
resultStack.sendActivityResultLocked(-1, resultRecord, resultWho, requestCode,
- RESULT_CANCELED, null);
+ RESULT_CANCELED, null, null);
}
// We pretend to the caller that it was really started, but
// they will just get a cancel result.
@@ -875,6 +909,10 @@
aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags,
null /*profilerInfo*/);
+ // The permissions review target shouldn't get any permission
+ // grants intended for the original destination
+ neededGrants = null;
+
if (DEBUG_PERMISSIONS_REVIEW) {
final ActivityStack focusedStack =
mRootActivityContainer.getTopDisplayFocusedStack();
@@ -897,6 +935,10 @@
callingPid = realCallingPid;
aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, null /*profilerInfo*/);
+
+ // The ephemeral installer shouldn't get any permission grants
+ // intended for the original destination
+ neededGrants = null;
}
ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid,
@@ -923,7 +965,7 @@
realCallingPid, realCallingUid, "Activity start")) {
if (!(restrictedBgActivity && handleBackgroundActivityAbort(r))) {
mController.addPendingActivityLaunch(new PendingActivityLaunch(r,
- sourceRecord, startFlags, stack, callerApp));
+ sourceRecord, startFlags, stack, callerApp, neededGrants));
}
ActivityOptions.abort(checkedOptions);
return ActivityManager.START_SWITCHES_CANCELED;
@@ -934,7 +976,8 @@
mController.doPendingActivityLaunches(false);
final int res = startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
- true /* doResume */, checkedOptions, inTask, outActivity, restrictedBgActivity);
+ true /* doResume */, checkedOptions, inTask, outActivity, restrictedBgActivity,
+ neededGrants);
mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(res, outActivity[0]);
return res;
}
@@ -1232,9 +1275,15 @@
}
}
}
+
// Collect information about the target of the Intent.
ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);
+ // Carefully collect grants without holding lock
+ NeededUriGrants neededGrants = mService.mUgmInternal.checkGrantUriPermissionFromIntent(
+ resolveCallingUid(mRequest.caller), intent, aInfo.applicationInfo.packageName,
+ UserHandle.getUserId(aInfo.applicationInfo.uid));
+
synchronized (mService.mGlobalLock) {
final ActivityStack stack = mRootActivityContainer.getTopDisplayFocusedStack();
stack.mConfigWillChange = globalConfig != null
@@ -1311,7 +1360,7 @@
callingUid, callingPackage, realCallingPid, realCallingUid, startFlags, options,
ignoreTargetSecurity, componentSpecified, outRecord, inTask, reason,
allowPendingRemoteAnimationRegistryLookup, originatingPendingIntent,
- allowBackgroundActivityStart);
+ allowBackgroundActivityStart, neededGrants);
Binder.restoreCallingIdentity(origId);
@@ -1406,14 +1455,16 @@
private int startActivity(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
- int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
- ActivityRecord[] outActivity, boolean restrictedBgActivity) {
+ int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
+ ActivityRecord[] outActivity, boolean restrictedBgActivity,
+ NeededUriGrants neededGrants) {
int result = START_CANCELED;
final ActivityStack startedActivityStack;
try {
mService.mWindowManager.deferSurfaceLayout();
result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,
- startFlags, doResume, options, inTask, outActivity, restrictedBgActivity);
+ startFlags, doResume, options, inTask, outActivity, restrictedBgActivity,
+ neededGrants);
} finally {
final ActivityStack currentStack = r.getActivityStack();
startedActivityStack = currentStack != null ? currentStack : mTargetStack;
@@ -1438,7 +1489,8 @@
final ActivityStack stack = mStartActivity.getActivityStack();
if (stack != null) {
stack.finishActivityLocked(mStartActivity, RESULT_CANCELED,
- null /* intentResultData */, "startActivity", true /* oomAdj */);
+ null /* resultData */, null /* resultGrants */,
+ "startActivity", true /* oomAdj */);
}
}
mService.mWindowManager.continueSurfaceLayout();
@@ -1467,7 +1519,7 @@
if (resultRecord != null) {
ActivityStack resultStack = resultRecord.getActivityStack();
resultStack.sendActivityResultLocked(-1, resultRecord, resultWho, requestCode,
- RESULT_CANCELED, null);
+ RESULT_CANCELED, null, null);
}
// We pretend to the caller that it was really started to make it backward compatible, but
// they will just get a cancel result.
@@ -1479,7 +1531,8 @@
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
- ActivityRecord[] outActivity, boolean restrictedBgActivity) {
+ ActivityRecord[] outActivity, boolean restrictedBgActivity,
+ NeededUriGrants neededGrants) {
setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
voiceInteractor, restrictedBgActivity);
@@ -1629,7 +1682,7 @@
if (sourceStack != null) {
sourceStack.sendActivityResultLocked(-1 /* callingUid */, mStartActivity.resultTo,
mStartActivity.resultWho, mStartActivity.requestCode, RESULT_CANCELED,
- null /* data */);
+ null /* resultData */, null /* resultGrants */);
}
ActivityOptions.abort(mOptions);
return START_CLASS_NOT_FOUND;
@@ -1696,8 +1749,8 @@
return result;
}
- mService.mUgmInternal.grantUriPermissionFromIntent(mCallingUid, mStartActivity.packageName,
- mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.mUserId);
+ mService.mUgmInternal.grantUriPermissionUncheckedFromIntent(neededGrants,
+ mStartActivity.getUriPermissionsLocked());
mService.getPackageManagerInternalLocked().grantEphemeralAccess(
mStartActivity.mUserId, mIntent, UserHandle.getAppId(mStartActivity.appInfo.uid),
UserHandle.getAppId(mCallingUid));
@@ -1933,7 +1986,7 @@
Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
sourceStack.sendActivityResultLocked(-1 /* callingUid */, mStartActivity.resultTo,
mStartActivity.resultWho, mStartActivity.requestCode, RESULT_CANCELED,
- null /* data */);
+ null /* resultData */, null /* resultGrants */);
mStartActivity.resultTo = null;
}
}
@@ -2362,8 +2415,13 @@
}
ActivityStack.logStartActivity(AM_NEW_INTENT, activity, activity.getTaskRecord());
- activity.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,
+
+ Intent intent = mStartActivity.intent;
+ NeededUriGrants intentGrants = mService.mUgmInternal.checkGrantUriPermissionFromIntent(
+ mCallingUid, intent, activity.packageName, activity.mUserId);
+ activity.deliverNewIntentLocked(mCallingUid, intent, intentGrants,
mStartActivity.launchedFromPackage);
+
mIntentDelivered = true;
}
@@ -2742,6 +2800,18 @@
}
}
+ private int resolveCallingUid(IApplicationThread caller) {
+ if (caller != null) {
+ synchronized (mService.mGlobalLock) {
+ final WindowProcessController callerApp = mService.getProcessController(caller);
+ if (callerApp != null) {
+ return callerApp.mInfo.uid;
+ }
+ }
+ }
+ return -1;
+ }
+
private boolean isLaunchModeOneOf(int mode1, int mode2) {
return mode1 == mLaunchMode || mode2 == mLaunchMode;
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index b97ecec..53cf9e4 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -265,6 +265,7 @@
import com.android.server.firewall.IntentFirewall;
import com.android.server.pm.UserManagerService;
import com.android.server.policy.PermissionPolicyInternal;
+import com.android.server.uri.NeededUriGrants;
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.vr.VrManagerInternal;
@@ -1540,11 +1541,19 @@
throw new IllegalArgumentException("File descriptors passed in Intent");
}
+ final ActivityRecord r;
synchronized (mGlobalLock) {
- ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ r = ActivityRecord.isInStackLocked(token);
if (r == null) {
return true;
}
+ }
+
+ // Carefully collect grants without holding lock
+ final NeededUriGrants resultGrants = mUgmInternal.checkGrantUriPermissionFromIntent(
+ Binder.getCallingUid(), resultData, r.packageName, r.mUserId);
+
+ synchronized (mGlobalLock) {
// Keep track of the root activity of the task before we finish it
final TaskRecord tr = r.getTaskRecord();
ActivityRecord rootR = tr.getRootActivity();
@@ -1606,7 +1615,7 @@
r.mRelaunchReason = RELAUNCH_REASON_NONE;
} else {
res = tr.getStack().requestFinishActivityLocked(token, resultCode,
- resultData, "app-request", true);
+ resultData, resultGrants, "app-request", true);
if (!res) {
Slog.i(TAG, "Failed to finish by app-request");
}
@@ -2132,14 +2141,23 @@
@Override
public boolean navigateUpTo(IBinder token, Intent destIntent, int resultCode,
Intent resultData) {
+ final ActivityRecord r;
+ synchronized (mGlobalLock) {
+ r = ActivityRecord.isInStackLocked(token);
+ if (r == null) {
+ return false;
+ }
+ }
+
+ // Carefully collect grants without holding lock
+ final NeededUriGrants destGrants = mUgmInternal.checkGrantUriPermissionFromIntent(
+ Binder.getCallingUid(), destIntent, r.packageName, r.mUserId);
+ final NeededUriGrants resultGrants = mUgmInternal.checkGrantUriPermissionFromIntent(
+ Binder.getCallingUid(), resultData, r.packageName, r.mUserId);
synchronized (mGlobalLock) {
- final ActivityRecord r = ActivityRecord.forTokenLocked(token);
- if (r != null) {
- return r.getActivityStack().navigateUpToLocked(
- r, destIntent, resultCode, resultData);
- }
- return false;
+ return r.getActivityStack().navigateUpToLocked(
+ r, destIntent, destGrants, resultCode, resultData, resultGrants);
}
}
@@ -6591,14 +6609,23 @@
@Override
public void sendActivityResult(int callingUid, IBinder activityToken, String resultWho,
- int requestCode, int resultCode, Intent data) {
+ int requestCode, int resultCode, Intent resultData) {
+ final ActivityRecord r;
synchronized (mGlobalLock) {
- final ActivityRecord r = ActivityRecord.isInStackLocked(activityToken);
- if (r != null && r.getActivityStack() != null) {
- r.getActivityStack().sendActivityResultLocked(callingUid, r, resultWho,
- requestCode, resultCode, data);
+ r = ActivityRecord.isInStackLocked(activityToken);
+ if (r == null || r.getActivityStack() == null) {
+ return;
}
}
+
+ // Carefully collect grants without holding lock
+ final NeededUriGrants resultGrants = mUgmInternal.checkGrantUriPermissionFromIntent(
+ Binder.getCallingUid(), resultData, r.packageName, r.mUserId);
+
+ synchronized (mGlobalLock) {
+ r.getActivityStack().sendActivityResultLocked(callingUid, r, resultWho,
+ requestCode, resultCode, resultData, resultGrants);
+ }
}
@Override
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index 298b302..4a0bf02a 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -1442,7 +1442,7 @@
mActivities.remove(activityNdx);
--activityNdx;
--numActivities;
- } else if (mStack.finishActivityLocked(r, Activity.RESULT_CANCELED, null,
+ } else if (mStack.finishActivityLocked(r, Activity.RESULT_CANCELED, null, null,
reason, false, pauseImmediately)) {
--activityNdx;
--numActivities;
@@ -1497,8 +1497,8 @@
if (opts != null) {
ret.updateOptionsLocked(opts);
}
- if (mStack != null && mStack.finishActivityLocked(
- r, Activity.RESULT_CANCELED, null, "clear-task-stack", false)) {
+ if (mStack != null && mStack.finishActivityLocked(r, Activity.RESULT_CANCELED,
+ null, null, "clear-task-stack", false)) {
--activityNdx;
--numActivities;
}
@@ -1512,8 +1512,8 @@
&& !ActivityStarter.isDocumentLaunchesIntoExisting(launchFlags)) {
if (!ret.finishing) {
if (mStack != null) {
- mStack.finishActivityLocked(
- ret, Activity.RESULT_CANCELED, null, "clear-task-top", false);
+ mStack.finishActivityLocked(ret, Activity.RESULT_CANCELED,
+ null, null, "clear-task-top", false);
}
return null;
}
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 2ad25cf..fbd4ec7 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -623,7 +623,7 @@
final ActivityRecord r = activities.get(i);
if (!r.finishing && r.isInStackLocked()) {
r.getActivityStack().finishActivityLocked(r, Activity.RESULT_CANCELED,
- null, "finish-heavy", true);
+ null, null, "finish-heavy", true);
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 5ef184a..de9d769 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1447,10 +1447,12 @@
void clearPolicyVisibilityFlag(int policyVisibilityFlag) {
mPolicyVisibility &= ~policyVisibilityFlag;
+ mWmService.scheduleAnimationLocked();
}
void setPolicyVisibilityFlag(int policyVisibilityFlag) {
mPolicyVisibility |= policyVisibilityFlag;
+ mWmService.scheduleAnimationLocked();
}
private boolean isLegacyPolicyVisibility() {
@@ -3889,7 +3891,7 @@
boolean performShowLocked() {
if (isHiddenFromUserLocked()) {
if (DEBUG_VISIBILITY) Slog.w(TAG, "hiding " + this + ", belonging to " + mOwnerUid);
- hideLw(false);
+ clearPolicyVisibilityFlag(VISIBLE_FOR_USER);
return false;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index 757267e5..4c0b582 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -940,7 +940,8 @@
homeStask.removeTask(homeTask, "testAdjustFocusedStack", REMOVE_TASK_MODE_DESTROYING);
// Finish the only activity.
- mStack.finishActivityLocked(topActivity, 0 /* resultCode */, null /* resultData */,
+ mStack.finishActivityLocked(topActivity, 0 /* resultCode */,
+ null /* resultData */, null /* resultGrants */,
"testAdjustFocusedStack", false /* oomAdj */);
// Although home stack is empty, it should still be the focused stack.
assertEquals(homeStask, mDefaultDisplay.getFocusedStack());
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
index a7bbe6e..2d12006 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
@@ -82,12 +82,12 @@
wpc.setThread(mock(IApplicationThread.class));
mController.addPendingActivityLaunch(
- new PendingActivityLaunch(activity, source, startFlags, stack, wpc));
+ new PendingActivityLaunch(activity, source, startFlags, stack, wpc, null));
final boolean resume = random.nextBoolean();
mController.doPendingActivityLaunches(resume);
verify(mStarter, times(1)).startResolvedActivity(eq(activity), eq(source), eq(null),
- eq(null), eq(startFlags), eq(resume), eq(null), eq(null));
+ eq(null), eq(startFlags), eq(resume), eq(null), eq(null), eq(null));
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index d77ea6e..e1ffb0f 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -77,7 +77,6 @@
import com.android.internal.util.Preconditions;
import com.android.server.FgThread;
import com.android.server.LocalServices;
-import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemService;
import com.android.server.UiThread;
import com.android.server.soundtrigger.SoundTriggerInternal;
@@ -1284,9 +1283,7 @@
mRm.addOnRoleHoldersChangedListenerAsUser(executor, this, UserHandle.ALL);
UserHandle currentUser = UserHandle.of(LocalServices.getService(
ActivityManagerInternal.class).getCurrentUserId());
- SystemServerInitThreadPool.get().submit(() -> onRoleHoldersChanged(
- RoleManager.ROLE_ASSISTANT, currentUser),
- "VoiceInteractionManagerService RoleObserver initialization");
+ onRoleHoldersChanged(RoleManager.ROLE_ASSISTANT, currentUser);
}
private @NonNull String getDefaultRecognizer(@NonNull UserHandle user) {
diff --git a/telephony/java/com/android/internal/telephony/SmsApplication.java b/telephony/java/com/android/internal/telephony/SmsApplication.java
index ef7c605..44dc24b 100644
--- a/telephony/java/com/android/internal/telephony/SmsApplication.java
+++ b/telephony/java/com/android/internal/telephony/SmsApplication.java
@@ -465,7 +465,11 @@
int userId) {
TelephonyManager tm = (TelephonyManager)
context.getSystemService(Context.TELEPHONY_SERVICE);
- if (!tm.isSmsCapable()) {
+ RoleManager roleManager = (RoleManager) context.getSystemService(Context.ROLE_SERVICE);
+ // (b/134400042) RoleManager might be null in unit tests running older mockito versions
+ // that do not support mocking final classes.
+ if (!tm.isSmsCapable() && (roleManager == null || !roleManager.isRoleAvailable(
+ RoleManager.ROLE_SMS))) {
// No phone, no SMS
return null;
}
@@ -584,7 +588,11 @@
public static void setDefaultApplicationAsUser(String packageName, Context context,
int userId) {
TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
- if (!tm.isSmsCapable()) {
+ RoleManager roleManager = (RoleManager) context.getSystemService(Context.ROLE_SERVICE);
+ // (b/134400042) RoleManager might be null in unit tests running older mockito versions
+ // that do not support mocking final classes.
+ if (!tm.isSmsCapable() && (roleManager == null || !roleManager.isRoleAvailable(
+ RoleManager.ROLE_SMS))) {
// No phone, no SMS
return;
}