Merge "Media - Don't animate during camera gesture"
diff --git a/api/test-current.txt b/api/test-current.txt
index 10a367f..a1d1fa7 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -970,6 +970,7 @@
method public boolean isSystemApp();
field public static final int PRIVATE_FLAG_PRIVILEGED = 8; // 0x8
field public int privateFlags;
+ field public int targetSandboxVersion;
}
public class LauncherApps {
@@ -1017,6 +1018,7 @@
field public static final String FEATURE_ADOPTABLE_STORAGE = "android.software.adoptable_storage";
field public static final String FEATURE_FILE_BASED_ENCRYPTION = "android.software.file_based_encryption";
field public static final int FLAG_PERMISSION_APPLY_RESTRICTION = 16384; // 0x4000
+ field public static final int FLAG_PERMISSION_GRANTED_BY_DEFAULT = 32; // 0x20
field public static final int FLAG_PERMISSION_GRANTED_BY_ROLE = 32768; // 0x8000
field public static final int FLAG_PERMISSION_ONE_TIME = 65536; // 0x10000
field public static final int FLAG_PERMISSION_POLICY_FIXED = 4; // 0x4
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 6f3e8922..6737972 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5187,19 +5187,11 @@
bindHeaderChronometerAndTime(contentView, p);
bindProfileBadge(contentView, p);
bindAlertedIcon(contentView, p);
- bindActivePermissions(contentView, p);
bindFeedbackIcon(contentView, p);
bindExpandButton(contentView, p);
mN.mUsesStandardHeader = true;
}
- private void bindActivePermissions(RemoteViews contentView, StandardTemplateParams p) {
- int color = getNeutralColor(p);
- contentView.setDrawableTint(R.id.camera, false, color, PorterDuff.Mode.SRC_ATOP);
- contentView.setDrawableTint(R.id.mic, false, color, PorterDuff.Mode.SRC_ATOP);
- contentView.setDrawableTint(R.id.overlay, false, color, PorterDuff.Mode.SRC_ATOP);
- }
-
private void bindFeedbackIcon(RemoteViews contentView, StandardTemplateParams p) {
int color = getNeutralColor(p);
contentView.setDrawableTint(R.id.feedback, false, color, PorterDuff.Mode.SRC_ATOP);
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 043953d..0c6810c 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1134,6 +1134,7 @@
* @hide
*/
@SystemApi
+ @TestApi
public int targetSandboxVersion;
/**
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index e08af55..7b2955d 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3373,6 +3373,7 @@
* @hide
*/
@SystemApi
+ @TestApi
public static final int FLAG_PERMISSION_GRANTED_BY_DEFAULT = 1 << 5;
/**
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index bf94670..6136a80 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -52,14 +52,12 @@
private View mHeaderText;
private View mSecondaryHeaderText;
private OnClickListener mExpandClickListener;
- private OnClickListener mAppOpsListener;
private OnClickListener mFeedbackListener;
private HeaderTouchListener mTouchListener = new HeaderTouchListener();
private LinearLayout mTransferChip;
private NotificationExpandButton mExpandButton;
private CachingIconView mIcon;
private View mProfileBadge;
- private View mAppOps;
private View mFeedbackIcon;
private boolean mExpanded;
private boolean mShowExpandButtonAtEnd;
@@ -117,7 +115,6 @@
mExpandButton = findViewById(com.android.internal.R.id.expand_button);
mIcon = findViewById(com.android.internal.R.id.icon);
mProfileBadge = findViewById(com.android.internal.R.id.profile_badge);
- mAppOps = findViewById(com.android.internal.R.id.app_ops);
mFeedbackIcon = findViewById(com.android.internal.R.id.feedback);
}
@@ -146,7 +143,6 @@
// Icons that should go at the end
if ((child == mExpandButton && mShowExpandButtonAtEnd)
|| child == mProfileBadge
- || child == mAppOps
|| child == mFeedbackIcon
|| child == mTransferChip) {
iconWidth += lp.leftMargin + lp.rightMargin + child.getMeasuredWidth();
@@ -212,7 +208,6 @@
// Icons that should go at the end
if ((child == mExpandButton && mShowExpandButtonAtEnd)
|| child == mProfileBadge
- || child == mAppOps
|| child == mFeedbackIcon
|| child == mTransferChip) {
if (end == getMeasuredWidth()) {
@@ -282,7 +277,7 @@
}
private void updateTouchListener() {
- if (mExpandClickListener == null && mAppOpsListener == null && mFeedbackListener == null) {
+ if (mExpandClickListener == null && mFeedbackListener == null) {
setOnTouchListener(null);
return;
}
@@ -291,14 +286,6 @@
}
/**
- * Sets onclick listener for app ops icons.
- */
- public void setAppOpsOnClickListener(OnClickListener l) {
- mAppOpsListener = l;
- updateTouchListener();
- }
-
- /**
* Sets onclick listener for feedback icon.
*/
public void setFeedbackOnClickListener(OnClickListener l) {
@@ -394,7 +381,6 @@
private final ArrayList<Rect> mTouchRects = new ArrayList<>();
private Rect mExpandButtonRect;
- private Rect mAppOpsRect;
private Rect mFeedbackRect;
private int mTouchSlop;
private boolean mTrackGesture;
@@ -408,9 +394,7 @@
mTouchRects.clear();
addRectAroundView(mIcon);
mExpandButtonRect = addRectAroundView(mExpandButton);
- mAppOpsRect = addRectAroundView(mAppOps);
mFeedbackRect = addRectAroundView(mFeedbackIcon);
- setTouchDelegate(new TouchDelegate(mAppOpsRect, mAppOps));
addWidthRect();
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
}
@@ -471,11 +455,7 @@
break;
case MotionEvent.ACTION_UP:
if (mTrackGesture) {
- if (mAppOps.isVisibleToUser() && (mAppOpsRect.contains((int) x, (int) y)
- || mAppOpsRect.contains((int) mDownX, (int) mDownY))) {
- mAppOps.performClick();
- return true;
- } else if (mFeedbackIcon.isVisibleToUser()
+ if (mFeedbackIcon.isVisibleToUser()
&& (mFeedbackRect.contains((int) x, (int) y))
|| mFeedbackRect.contains((int) mDownX, (int) mDownY)) {
mFeedbackIcon.performClick();
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index 3332143..289a36f 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -168,8 +168,6 @@
private int mFacePileProtectionWidthExpanded;
private boolean mImportantConversation;
private TextView mUnreadBadge;
- private ViewGroup mAppOps;
- private Rect mAppOpsTouchRect = new Rect();
private View mFeedbackIcon;
private float mMinTouchSize;
private Icon mConversationIcon;
@@ -214,7 +212,6 @@
mConversationIconView = findViewById(R.id.conversation_icon);
mConversationIconContainer = findViewById(R.id.conversation_icon_container);
mIcon = findViewById(R.id.icon);
- mAppOps = findViewById(com.android.internal.R.id.app_ops);
mFeedbackIcon = findViewById(com.android.internal.R.id.feedback);
mMinTouchSize = 48 * getResources().getDisplayMetrics().density;
mImportanceRingView = findViewById(R.id.conversation_icon_badge_ring);
@@ -1174,43 +1171,6 @@
});
}
mTouchDelegate.clear();
- if (mAppOps.getWidth() > 0) {
-
- // Let's increase the touch size of the app ops view if it's here
- mAppOpsTouchRect.set(
- mAppOps.getLeft(),
- mAppOps.getTop(),
- mAppOps.getRight(),
- mAppOps.getBottom());
- for (int i = 0; i < mAppOps.getChildCount(); i++) {
- View child = mAppOps.getChildAt(i);
- if (child.getVisibility() == GONE) {
- continue;
- }
- // Make sure each child has at least a minTouchSize touch target around it
- float childTouchLeft = child.getLeft() + child.getWidth() / 2.0f
- - mMinTouchSize / 2.0f;
- float childTouchRight = childTouchLeft + mMinTouchSize;
- mAppOpsTouchRect.left = (int) Math.min(mAppOpsTouchRect.left,
- mAppOps.getLeft() + childTouchLeft);
- mAppOpsTouchRect.right = (int) Math.max(mAppOpsTouchRect.right,
- mAppOps.getLeft() + childTouchRight);
- }
-
- // Increase the height
- int heightIncrease = 0;
- if (mAppOpsTouchRect.height() < mMinTouchSize) {
- heightIncrease = (int) Math.ceil((mMinTouchSize - mAppOpsTouchRect.height())
- / 2.0f);
- }
- mAppOpsTouchRect.inset(0, -heightIncrease);
-
- getRelativeTouchRect(mAppOpsTouchRect, mAppOps);
-
- // Extend the size of the app opps to be at least 48dp
- mTouchDelegate.add(new TouchDelegate(mAppOpsTouchRect, mAppOps));
-
- }
if (mFeedbackIcon.getVisibility() == VISIBLE) {
updateFeedbackIconMargins();
float width = Math.max(mMinTouchSize, mFeedbackIcon.getWidth());
@@ -1240,13 +1200,7 @@
private void updateFeedbackIconMargins() {
MarginLayoutParams lp = (MarginLayoutParams) mFeedbackIcon.getLayoutParams();
- if (mAppOps.getWidth() == 0) {
- lp.setMarginStart(mNotificationHeaderSeparatingMargin);
- } else {
- float width = Math.max(mMinTouchSize, mFeedbackIcon.getWidth());
- int horizontalMargin = (int) ((width - mFeedbackIcon.getWidth()) / 2);
- lp.setMarginStart(horizontalMargin);
- }
+ lp.setMarginStart(mNotificationHeaderSeparatingMargin);
mFeedbackIcon.setLayoutParams(lp);
}
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index c73441c..f96ed36 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -1790,8 +1790,7 @@
#ifdef ANDROID_EXPERIMENTAL_MTE
SetTagCheckingLevel(PR_MTE_TCF_SYNC);
#endif
- // TODO(pcc): Use SYNC here once the allocator supports it.
- heap_tagging_level = M_HEAP_TAGGING_LEVEL_ASYNC;
+ heap_tagging_level = M_HEAP_TAGGING_LEVEL_SYNC;
break;
default:
#ifdef ANDROID_EXPERIMENTAL_MTE
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index d22a19f..9a1b592 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -160,43 +160,6 @@
android:visibility="gone"
android:contentDescription="@string/notification_work_profile_content_description"
/>
- <LinearLayout
- android:id="@+id/app_ops"
- android:layout_height="match_parent"
- android:layout_width="wrap_content"
- android:layout_marginStart="6dp"
- android:background="?android:selectableItemBackgroundBorderless"
- android:orientation="horizontal">
- <ImageView
- android:id="@+id/camera"
- android:layout_width="?attr/notificationHeaderIconSize"
- android:layout_height="?attr/notificationHeaderIconSize"
- android:src="@drawable/ic_camera"
- android:visibility="gone"
- android:focusable="false"
- android:contentDescription="@string/notification_appops_camera_active"
- />
- <ImageView
- android:id="@+id/mic"
- android:layout_width="?attr/notificationHeaderIconSize"
- android:layout_height="?attr/notificationHeaderIconSize"
- android:src="@drawable/ic_mic"
- android:layout_marginStart="4dp"
- android:visibility="gone"
- android:focusable="false"
- android:contentDescription="@string/notification_appops_microphone_active"
- />
- <ImageView
- android:id="@+id/overlay"
- android:layout_width="?attr/notificationHeaderIconSize"
- android:layout_height="?attr/notificationHeaderIconSize"
- android:src="@drawable/ic_alert_window_layer"
- android:layout_marginStart="4dp"
- android:visibility="gone"
- android:focusable="false"
- android:contentDescription="@string/notification_appops_overlay_active"
- />
- </LinearLayout>
<include
layout="@layout/notification_material_media_transfer_action"
android:id="@+id/media_seamless"
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
index 2a96e09..51ad286 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
@@ -29,8 +29,8 @@
import com.android.systemui.car.CarDeviceProvisionedController;
import com.android.systemui.car.CarDeviceProvisionedControllerImpl;
import com.android.systemui.car.keyguard.CarKeyguardViewController;
+import com.android.systemui.car.notification.NotificationShadeWindowControllerImpl;
import com.android.systemui.car.statusbar.DozeServiceHost;
-import com.android.systemui.car.statusbar.DummyNotificationShadeWindowController;
import com.android.systemui.car.volume.CarVolumeDialogComponent;
import com.android.systemui.dagger.GlobalRootComponent;
import com.android.systemui.dagger.qualifiers.Background;
@@ -53,12 +53,12 @@
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.ShadeControllerImpl;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -209,6 +209,10 @@
CarKeyguardViewController carKeyguardViewController);
@Binds
+ abstract NotificationShadeWindowController bindNotificationShadeController(
+ NotificationShadeWindowControllerImpl notificationPanelViewController);
+
+ @Binds
abstract DeviceProvisionedController bindDeviceProvisionedController(
CarDeviceProvisionedControllerImpl deviceProvisionedController);
@@ -217,9 +221,5 @@
CarDeviceProvisionedControllerImpl deviceProvisionedController);
@Binds
- abstract NotificationShadeWindowController bindNotificationShadeWindowController(
- DummyNotificationShadeWindowController notificationShadeWindowController);
-
- @Binds
abstract DozeHost bindDozeHost(DozeServiceHost dozeServiceHost);
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationShadeWindowControllerImpl.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationShadeWindowControllerImpl.java
new file mode 100644
index 0000000..0c064bd
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationShadeWindowControllerImpl.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2020 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.systemui.car.notification;
+
+import com.android.systemui.car.window.OverlayViewGlobalStateController;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/** The automotive version of the notification shade window controller. */
+@Singleton
+public class NotificationShadeWindowControllerImpl implements
+ NotificationShadeWindowController {
+
+ private final OverlayViewGlobalStateController mController;
+
+ @Inject
+ public NotificationShadeWindowControllerImpl(OverlayViewGlobalStateController controller) {
+ mController = controller;
+ }
+
+ @Override
+ public void setForceDozeBrightness(boolean forceDozeBrightness) {
+ // No-op since dozing is not supported in Automotive devices.
+ }
+
+ @Override
+ public void setNotificationShadeFocusable(boolean focusable) {
+ mController.setWindowFocusable(focusable);
+ }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/DummyNotificationShadeWindowController.java b/packages/CarSystemUI/src/com/android/systemui/car/statusbar/DummyNotificationShadeWindowController.java
deleted file mode 100644
index 13f2b7e..0000000
--- a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/DummyNotificationShadeWindowController.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2020 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.systemui.car.statusbar;
-
-import android.app.IActivityManager;
-import android.content.Context;
-import android.view.WindowManager;
-
-import com.android.systemui.car.window.SystemUIOverlayWindowController;
-import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.keyguard.KeyguardViewMediator;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.phone.BiometricUnlockController;
-import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
-/**
- * A dummy implementation of {@link NotificationShadeWindowController}.
- *
- * TODO(b/155711562): This should be replaced with a longer term solution (i.e. separating
- * {@link BiometricUnlockController} from the views it depends on).
- */
-@Singleton
-public class DummyNotificationShadeWindowController extends NotificationShadeWindowController {
- private final SystemUIOverlayWindowController mOverlayWindowController;
-
- @Inject
- public DummyNotificationShadeWindowController(Context context,
- WindowManager windowManager, IActivityManager activityManager,
- DozeParameters dozeParameters,
- StatusBarStateController statusBarStateController,
- ConfigurationController configurationController,
- KeyguardViewMediator keyguardViewMediator,
- KeyguardBypassController keyguardBypassController,
- SysuiColorExtractor colorExtractor,
- DumpManager dumpManager,
- SystemUIOverlayWindowController overlayWindowController) {
- super(context, windowManager, activityManager, dozeParameters, statusBarStateController,
- configurationController, keyguardViewMediator, keyguardBypassController,
- colorExtractor, dumpManager);
- mOverlayWindowController = overlayWindowController;
- }
-
- @Override
- public void setForceDozeBrightness(boolean forceDozeBrightness) {
- // No op.
- }
-
- @Override
- public void setNotificationShadeFocusable(boolean focusable) {
- // The overlay window is the car sysui equivalent of the notification shade.
- mOverlayWindowController.setWindowFocusable(focusable);
- }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index b7ae3dc..bc58bfc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -189,10 +189,12 @@
}
}
updateStatusLabel();
+ mCallback.run();
} else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
// Default to -200 as its below WifiManager.MIN_RSSI.
updateRssi(intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200));
updateStatusLabel();
+ mCallback.run();
}
}
@@ -218,13 +220,15 @@
return;
}
NetworkCapabilities networkCapabilities;
- final Network currentWifiNetwork = mWifiManager.getCurrentNetwork();
- if (currentWifiNetwork != null && currentWifiNetwork.equals(mDefaultNetwork)) {
+ isDefaultNetwork = false;
+ if (mDefaultNetworkCapabilities != null) {
+ isDefaultNetwork = mDefaultNetworkCapabilities.hasTransport(
+ NetworkCapabilities.TRANSPORT_WIFI);
+ }
+ if (isDefaultNetwork) {
// Wifi is connected and the default network.
- isDefaultNetwork = true;
networkCapabilities = mDefaultNetworkCapabilities;
} else {
- isDefaultNetwork = false;
networkCapabilities = mConnectivityManager.getNetworkCapabilities(
mWifiManager.getCurrentNetwork());
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java
index d2112a0..883f4de 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java
@@ -75,11 +75,6 @@
public MenuItem getLongpressMenuItem(Context context);
/**
- * @return the {@link MenuItem} to display when app ops icons are pressed.
- */
- public MenuItem getAppOpsMenuItem(Context context);
-
- /**
* @return the {@link MenuItem} to display when feedback icon is pressed.
*/
public MenuItem getFeedbackMenuItem(Context context);
diff --git a/packages/SystemUI/res/values/config_tv.xml b/packages/SystemUI/res/values/config_tv.xml
index 5cb840f..7451ba8 100644
--- a/packages/SystemUI/res/values/config_tv.xml
+++ b/packages/SystemUI/res/values/config_tv.xml
@@ -22,9 +22,4 @@
<!-- Whether to enable microphone disclosure indicator
(com.android.systemui.statusbar.tv.micdisclosure.AudioRecordingDisclosureBar). -->
<bool name="audio_recording_disclosure_enabled">true</bool>
-
- <!-- Whether the indicator should expand and show the recording application's label.
- When disabled (false) the "minimized" indicator would appear on the screen whenever an
- application is recording, but will not reveal to the user what application this is. -->
- <bool name="audio_recording_disclosure_reveal_packages">false</bool>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 10f2069..c04775a 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -47,6 +47,8 @@
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.model.SysUiState;
+import com.android.systemui.navigationbar.NavigationBarController;
+import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.PluginDependencyProvider;
@@ -64,11 +66,11 @@
import com.android.systemui.shared.system.PackageManagerWrapper;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.NotificationViewHierarchyManager;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.VibratorHelper;
@@ -85,10 +87,8 @@
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
import com.android.systemui.statusbar.phone.ManagedProfileController;
-import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarIconController;
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java
index 2deeb12..5f88156 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java
@@ -39,21 +39,15 @@
*/
@Singleton
public class ForegroundServiceController {
- public static final int[] APP_OPS = new int[] {AppOpsManager.OP_CAMERA,
- AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
- AppOpsManager.OP_RECORD_AUDIO,
- AppOpsManager.OP_COARSE_LOCATION,
- AppOpsManager.OP_FINE_LOCATION};
+ public static final int[] APP_OPS = new int[] {AppOpsManager.OP_SYSTEM_ALERT_WINDOW};
private final SparseArray<ForegroundServicesUserState> mUserServices = new SparseArray<>();
private final Object mMutex = new Object();
- private final NotificationEntryManager mEntryManager;
private final Handler mMainHandler;
@Inject
- public ForegroundServiceController(NotificationEntryManager entryManager,
- AppOpsController appOpsController, @Main Handler mainHandler) {
- mEntryManager = entryManager;
+ public ForegroundServiceController(AppOpsController appOpsController,
+ @Main Handler mainHandler) {
mMainHandler = mainHandler;
appOpsController.addCallback(APP_OPS, (code, uid, packageName, active) -> {
mMainHandler.post(() -> {
@@ -87,19 +81,6 @@
}
/**
- * Returns the keys for notifications from this package using the standard template,
- * if they exist.
- */
- @Nullable
- public ArraySet<String> getStandardLayoutKeys(int userId, String pkg) {
- synchronized (mMutex) {
- final ForegroundServicesUserState services = mUserServices.get(userId);
- if (services == null) return null;
- return services.getStandardLayoutKeys(pkg);
- }
- }
-
- /**
* Gets active app ops for this user and package
*/
@Nullable
@@ -140,31 +121,6 @@
userServices.removeOp(packageName, appOpCode);
}
}
-
- // TODO: (b/145659174) remove when moving to NewNotifPipeline. Replaced by
- // AppOpsCoordinator
- // Update appOps if there are associated pending or visible notifications
- final Set<String> notificationKeys = getStandardLayoutKeys(userId, packageName);
- if (notificationKeys != null) {
- boolean changed = false;
- for (String key : notificationKeys) {
- final NotificationEntry entry = mEntryManager.getPendingOrActiveNotif(key);
- if (entry != null
- && uid == entry.getSbn().getUid()
- && packageName.equals(entry.getSbn().getPackageName())) {
- synchronized (entry.mActiveAppOps) {
- if (active) {
- changed |= entry.mActiveAppOps.add(appOpCode);
- } else {
- changed |= entry.mActiveAppOps.remove(appOpCode);
- }
- }
- }
- }
- if (changed) {
- mEntryManager.updateNotifications("appOpChanged pkg=" + packageName);
- }
- }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
index bb44583..1515272 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
@@ -172,24 +172,8 @@
sbn.getPackageName(), sbn.getKey());
}
}
- tagAppOps(entry);
return true;
},
true /* create if not found */);
}
-
- // TODO: (b/145659174) remove when moving to NewNotifPipeline. Replaced by
- // AppOpsCoordinator
- private void tagAppOps(NotificationEntry entry) {
- final StatusBarNotification sbn = entry.getSbn();
- ArraySet<Integer> activeOps = mForegroundServiceController.getAppOps(
- sbn.getUserId(),
- sbn.getPackageName());
- synchronized (entry.mActiveAppOps) {
- entry.mActiveAppOps.clear();
- if (activeOps != null) {
- entry.mActiveAppOps.addAll(activeOps);
- }
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 9c5f9fe..9e9d85a 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -84,7 +84,6 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dumpable;
-import com.android.systemui.bubbles.animation.StackAnimationController;
import com.android.systemui.bubbles.dagger.BubbleModule;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.model.SysUiState;
@@ -96,6 +95,7 @@
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationRemoveInterceptor;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.statusbar.notification.NotificationChannelHelper;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
@@ -109,7 +109,6 @@
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
index 10d301d..fcb215c 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
@@ -30,11 +30,11 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ZenModeController;
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
index 251ce13..4a5d142 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
@@ -43,6 +43,7 @@
import android.hardware.display.DisplayManager;
import android.media.AudioManager;
import android.media.MediaRouter2Manager;
+import android.media.session.MediaSessionManager;
import android.net.ConnectivityManager;
import android.net.NetworkScoreManager;
import android.net.wifi.WifiManager;
@@ -226,6 +227,11 @@
}
@Provides
+ static MediaSessionManager provideMediaSessionManager(Context context) {
+ return context.getSystemService(MediaSessionManager.class);
+ }
+
+ @Provides
@Singleton
static NetworkScoreManager provideNetworkScoreManager(Context context) {
return context.getSystemService(NetworkScoreManager.class);
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
index 56219c3..b4e6e0e 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
@@ -45,12 +45,14 @@
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.phone.DozeServiceHost;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.NotificationShadeWindowControllerImpl;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.ShadeControllerImpl;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -172,5 +174,9 @@
StatusBarKeyguardViewManager statusBarKeyguardViewManager);
@Binds
+ abstract NotificationShadeWindowController bindNotificationShadeController(
+ NotificationShadeWindowControllerImpl notificationShadeWindowController);
+
+ @Binds
abstract DozeHost provideDozeHost(DozeServiceHost dozeServiceHost);
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index ef51abb..d213ac1 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -132,7 +132,7 @@
import com.android.systemui.plugins.GlobalActionsPanelPlugin;
import com.android.systemui.settings.CurrentUserContextTracker;
import com.android.systemui.statusbar.NotificationShadeDepthController;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.EmergencyDialerConstants;
@@ -148,6 +148,7 @@
import java.util.concurrent.Executor;
import javax.inject.Inject;
+import javax.inject.Provider;
/**
* Helper to show the global actions dialog. Each item is an {@link Action} that may show depending
@@ -401,7 +402,7 @@
if (mDialog != null) {
if (!mDialog.isShowingControls() && shouldShowControls()) {
mDialog.showControls(mControlsUiControllerOptional.get());
- } else if (shouldShowLockMessage()) {
+ } else if (shouldShowLockMessage(mDialog)) {
mDialog.showLockMessage();
}
}
@@ -698,19 +699,17 @@
mPowerAdapter = new MyPowerOptionsAdapter();
mDepthController.setShowingHomeControls(true);
- GlobalActionsPanelPlugin.PanelViewController walletViewController =
- getWalletViewController();
ControlsUiController uiController = null;
if (mControlsUiControllerOptional.isPresent() && shouldShowControls()) {
uiController = mControlsUiControllerOptional.get();
}
ActionsDialog dialog = new ActionsDialog(mContext, mAdapter, mOverflowAdapter,
- walletViewController, mDepthController, mSysuiColorExtractor,
+ this::getWalletViewController, mDepthController, mSysuiColorExtractor,
mStatusBarService, mNotificationShadeWindowController,
controlsAvailable(), uiController,
mSysUiState, this::onRotate, mKeyguardShowing, mPowerAdapter);
- if (shouldShowLockMessage()) {
+ if (shouldShowLockMessage(dialog)) {
dialog.showLockMessage();
}
dialog.setCanceledOnTouchOutside(false); // Handled by the custom class.
@@ -2124,7 +2123,8 @@
private MultiListLayout mGlobalActionsLayout;
private Drawable mBackgroundDrawable;
private final SysuiColorExtractor mColorExtractor;
- private final GlobalActionsPanelPlugin.PanelViewController mWalletViewController;
+ private final Provider<GlobalActionsPanelPlugin.PanelViewController> mWalletFactory;
+ @Nullable private GlobalActionsPanelPlugin.PanelViewController mWalletViewController;
private boolean mKeyguardShowing;
private boolean mShowing;
private float mScrimAlpha;
@@ -2144,7 +2144,7 @@
private TextView mLockMessage;
ActionsDialog(Context context, MyAdapter adapter, MyOverflowAdapter overflowAdapter,
- GlobalActionsPanelPlugin.PanelViewController walletViewController,
+ Provider<GlobalActionsPanelPlugin.PanelViewController> walletFactory,
NotificationShadeDepthController depthController,
SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
NotificationShadeWindowController notificationShadeWindowController,
@@ -2165,6 +2165,7 @@
mSysUiState = sysuiState;
mOnRotateCallback = onRotateCallback;
mKeyguardShowing = keyguardShowing;
+ mWalletFactory = walletFactory;
// Window initialization
Window window = getWindow();
@@ -2187,7 +2188,6 @@
window.getAttributes().setFitInsetsTypes(0 /* types */);
setTitle(R.string.global_actions);
- mWalletViewController = walletViewController;
initializeLayout();
}
@@ -2200,8 +2200,13 @@
mControlsUiController.show(mControlsView, this::dismissForControlsActivity);
}
+ private boolean isWalletViewAvailable() {
+ return mWalletViewController != null && mWalletViewController.getPanelContent() != null;
+ }
+
private void initializeWalletView() {
- if (mWalletViewController == null || mWalletViewController.getPanelContent() == null) {
+ mWalletViewController = mWalletFactory.get();
+ if (!isWalletViewAvailable()) {
return;
}
@@ -2507,6 +2512,8 @@
private void dismissWallet() {
if (mWalletViewController != null) {
mWalletViewController.onDismissed();
+ // The wallet controller should not be re-used after being dismissed.
+ mWalletViewController = null;
}
}
@@ -2648,18 +2655,12 @@
&& !mControlsServiceInfos.isEmpty();
}
- private boolean walletViewAvailable() {
- GlobalActionsPanelPlugin.PanelViewController walletViewController =
- getWalletViewController();
- return walletViewController != null && walletViewController.getPanelContent() != null;
- }
-
- private boolean shouldShowLockMessage() {
+ private boolean shouldShowLockMessage(ActionsDialog dialog) {
boolean isLockedAfterBoot = mLockPatternUtils.getStrongAuthForUser(getCurrentUser().id)
== STRONG_AUTH_REQUIRED_AFTER_BOOT;
return !mKeyguardStateController.isUnlocked()
&& (!mShowLockScreenCardsAndControls || isLockedAfterBoot)
- && (controlsAvailable() || walletViewAvailable());
+ && (controlsAvailable() || dialog.isWalletViewAvailable());
}
private void onPowerMenuLockScreenSettingsChanged() {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index b993faa..b13be7b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -42,7 +42,7 @@
private val mediaHostStatesManager: MediaHostStatesManager,
private val activityStarter: ActivityStarter,
@Main executor: DelayableExecutor,
- mediaManager: MediaDataFilter,
+ mediaManager: MediaDataManager,
configurationController: ConfigurationController,
falsingManager: FalsingManager
) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt
index d0642cc..aa3699e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt
@@ -17,65 +17,48 @@
package com.android.systemui.media
import javax.inject.Inject
-import javax.inject.Singleton
/**
- * Combines updates from [MediaDataManager] with [MediaDeviceManager].
+ * Combines [MediaDataManager.Listener] events with [MediaDeviceManager.Listener] events.
*/
-@Singleton
-class MediaDataCombineLatest @Inject constructor(
- private val dataSource: MediaDataManager,
- private val deviceSource: MediaDeviceManager
-) {
+class MediaDataCombineLatest @Inject constructor() : MediaDataManager.Listener,
+ MediaDeviceManager.Listener {
+
private val listeners: MutableSet<MediaDataManager.Listener> = mutableSetOf()
private val entries: MutableMap<String, Pair<MediaData?, MediaDeviceData?>> = mutableMapOf()
- init {
- dataSource.addListener(object : MediaDataManager.Listener {
- override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) {
- if (oldKey != null && oldKey != key && entries.contains(oldKey)) {
- entries[key] = data to entries.remove(oldKey)?.second
- update(key, oldKey)
- } else {
- entries[key] = data to entries[key]?.second
- update(key, key)
- }
- }
- override fun onMediaDataRemoved(key: String) {
- remove(key)
- }
- })
- deviceSource.addListener(object : MediaDeviceManager.Listener {
- override fun onMediaDeviceChanged(
- key: String,
- oldKey: String?,
- data: MediaDeviceData?
- ) {
- if (oldKey != null && oldKey != key && entries.contains(oldKey)) {
- entries[key] = entries.remove(oldKey)?.first to data
- update(key, oldKey)
- } else {
- entries[key] = entries[key]?.first to data
- update(key, key)
- }
- }
- override fun onKeyRemoved(key: String) {
- remove(key)
- }
- })
+ override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) {
+ if (oldKey != null && oldKey != key && entries.contains(oldKey)) {
+ entries[key] = data to entries.remove(oldKey)?.second
+ update(key, oldKey)
+ } else {
+ entries[key] = data to entries[key]?.second
+ update(key, key)
+ }
}
- /**
- * Get a map of all non-null data entries
- */
- fun getData(): Map<String, MediaData> {
- return entries.filter {
- (key, pair) -> pair.first != null && pair.second != null
- }.mapValues {
- (key, pair) -> pair.first!!.copy(device = pair.second)
+ override fun onMediaDataRemoved(key: String) {
+ remove(key)
+ }
+
+ override fun onMediaDeviceChanged(
+ key: String,
+ oldKey: String?,
+ data: MediaDeviceData?
+ ) {
+ if (oldKey != null && oldKey != key && entries.contains(oldKey)) {
+ entries[key] = entries.remove(oldKey)?.first to data
+ update(key, oldKey)
+ } else {
+ entries[key] = entries[key]?.first to data
+ update(key, key)
}
}
+ override fun onKeyRemoved(key: String) {
+ remove(key)
+ }
+
/**
* Add a listener for [MediaData] changes that has been combined with latest [MediaDeviceData].
*/
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
index 24ca970..0664a41 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
@@ -24,7 +24,6 @@
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import java.util.concurrent.Executor
import javax.inject.Inject
-import javax.inject.Singleton
private const val TAG = "MediaDataFilter"
private const val DEBUG = true
@@ -33,24 +32,24 @@
* Filters data updates from [MediaDataCombineLatest] based on the current user ID, and handles user
* switches (removing entries for the previous user, adding back entries for the current user)
*
- * This is added downstream of [MediaDataManager] since we may still need to handle callbacks from
- * background users (e.g. timeouts) that UI classes should ignore.
- * Instead, UI classes should listen to this so they can stay in sync with the current user.
+ * This is added at the end of the pipeline since we may still need to handle callbacks from
+ * background users (e.g. timeouts).
*/
-@Singleton
class MediaDataFilter @Inject constructor(
- private val dataSource: MediaDataCombineLatest,
private val broadcastDispatcher: BroadcastDispatcher,
private val mediaResumeListener: MediaResumeListener,
- private val mediaDataManager: MediaDataManager,
private val lockscreenUserManager: NotificationLockscreenUserManager,
@Main private val executor: Executor
) : MediaDataManager.Listener {
private val userTracker: CurrentUserTracker
- private val listeners: MutableSet<MediaDataManager.Listener> = mutableSetOf()
+ private val _listeners: MutableSet<MediaDataManager.Listener> = mutableSetOf()
+ internal val listeners: Set<MediaDataManager.Listener>
+ get() = _listeners.toSet()
+ internal lateinit var mediaDataManager: MediaDataManager
- // The filtered mediaEntries, which will be a subset of all mediaEntries in MediaDataManager
- private val mediaEntries: LinkedHashMap<String, MediaData> = LinkedHashMap()
+ private val allEntries: LinkedHashMap<String, MediaData> = LinkedHashMap()
+ // The filtered userEntries, which will be a subset of all userEntries in MediaDataManager
+ private val userEntries: LinkedHashMap<String, MediaData> = LinkedHashMap()
init {
userTracker = object : CurrentUserTracker(broadcastDispatcher) {
@@ -60,31 +59,34 @@
}
}
userTracker.startTracking()
- dataSource.addListener(this)
}
override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) {
+ if (oldKey != null && oldKey != key) {
+ allEntries.remove(oldKey)
+ }
+ allEntries.put(key, data)
+
if (!lockscreenUserManager.isCurrentProfile(data.userId)) {
return
}
- if (oldKey != null) {
- mediaEntries.remove(oldKey)
+ if (oldKey != null && oldKey != key) {
+ userEntries.remove(oldKey)
}
- mediaEntries.put(key, data)
+ userEntries.put(key, data)
// Notify listeners
- val listenersCopy = listeners.toSet()
- listenersCopy.forEach {
+ listeners.forEach {
it.onMediaDataLoaded(key, oldKey, data)
}
}
override fun onMediaDataRemoved(key: String) {
- mediaEntries.remove(key)?.let {
+ allEntries.remove(key)
+ userEntries.remove(key)?.let {
// Only notify listeners if something actually changed
- val listenersCopy = listeners.toSet()
- listenersCopy.forEach {
+ listeners.forEach {
it.onMediaDataRemoved(key)
}
}
@@ -93,11 +95,11 @@
@VisibleForTesting
internal fun handleUserSwitched(id: Int) {
// If the user changes, remove all current MediaData objects and inform listeners
- val listenersCopy = listeners.toSet()
- val keyCopy = mediaEntries.keys.toMutableList()
+ val listenersCopy = listeners
+ val keyCopy = userEntries.keys.toMutableList()
// Clear the list first, to make sure callbacks from listeners if we have any entries
// are up to date
- mediaEntries.clear()
+ userEntries.clear()
keyCopy.forEach {
if (DEBUG) Log.d(TAG, "Removing $it after user change")
listenersCopy.forEach { listener ->
@@ -105,10 +107,10 @@
}
}
- dataSource.getData().forEach { (key, data) ->
+ allEntries.forEach { (key, data) ->
if (lockscreenUserManager.isCurrentProfile(data.userId)) {
if (DEBUG) Log.d(TAG, "Re-adding $key after user change")
- mediaEntries.put(key, data)
+ userEntries.put(key, data)
listenersCopy.forEach { listener ->
listener.onMediaDataLoaded(key, null, data)
}
@@ -121,7 +123,7 @@
*/
fun onSwipeToDismiss() {
if (DEBUG) Log.d(TAG, "Media carousel swiped away")
- val mediaKeys = mediaEntries.keys.toSet()
+ val mediaKeys = userEntries.keys.toSet()
mediaKeys.forEach {
mediaDataManager.setTimedOut(it, timedOut = true)
}
@@ -130,7 +132,7 @@
/**
* Are there any media notifications active?
*/
- fun hasActiveMedia() = mediaEntries.any { it.value.active }
+ fun hasActiveMedia() = userEntries.any { it.value.active }
/**
* Are there any media entries we should display?
@@ -138,7 +140,7 @@
* If resumption is disabled, we only want to show active players
*/
fun hasAnyMedia() = if (mediaResumeListener.isResumptionEnabled()) {
- mediaEntries.isNotEmpty()
+ userEntries.isNotEmpty()
} else {
hasActiveMedia()
}
@@ -146,10 +148,10 @@
/**
* Add a listener for filtered [MediaData] changes
*/
- fun addListener(listener: MediaDataManager.Listener) = listeners.add(listener)
+ fun addListener(listener: MediaDataManager.Listener) = _listeners.add(listener)
/**
* Remove a listener that was registered with addListener
*/
- fun removeListener(listener: MediaDataManager.Listener) = listeners.remove(listener)
-}
\ No newline at end of file
+ fun removeListener(listener: MediaDataManager.Listener) = _listeners.remove(listener)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index bff334e..e239ba9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -101,12 +101,23 @@
dumpManager: DumpManager,
mediaTimeoutListener: MediaTimeoutListener,
mediaResumeListener: MediaResumeListener,
+ mediaSessionBasedFilter: MediaSessionBasedFilter,
+ mediaDeviceManager: MediaDeviceManager,
+ mediaDataCombineLatest: MediaDataCombineLatest,
+ private val mediaDataFilter: MediaDataFilter,
private val activityStarter: ActivityStarter,
private var useMediaResumption: Boolean,
private val useQsMediaPlayer: Boolean
) : Dumpable {
- private val listeners: MutableSet<Listener> = mutableSetOf()
+ // Internal listeners are part of the internal pipeline. External listeners (those registered
+ // with [MediaDeviceManager.addListener]) receive events after they have propagated through
+ // the internal pipeline.
+ // Another way to think of the distinction between internal and external listeners is the
+ // following. Internal listeners are listeners that MediaDataManager depends on, and external
+ // listeners are listeners that depend on MediaDataManager.
+ // TODO(b/159539991#comment5): Move internal listeners to separate package.
+ private val internalListeners: MutableSet<Listener> = mutableSetOf()
private val mediaEntries: LinkedHashMap<String, MediaData> = LinkedHashMap()
internal var appsBlockedFromResume: MutableSet<String> = Utils.getBlockedMediaApps(context)
set(value) {
@@ -130,9 +141,14 @@
broadcastDispatcher: BroadcastDispatcher,
mediaTimeoutListener: MediaTimeoutListener,
mediaResumeListener: MediaResumeListener,
+ mediaSessionBasedFilter: MediaSessionBasedFilter,
+ mediaDeviceManager: MediaDeviceManager,
+ mediaDataCombineLatest: MediaDataCombineLatest,
+ mediaDataFilter: MediaDataFilter,
activityStarter: ActivityStarter
) : this(context, backgroundExecutor, foregroundExecutor, mediaControllerFactory,
broadcastDispatcher, dumpManager, mediaTimeoutListener, mediaResumeListener,
+ mediaSessionBasedFilter, mediaDeviceManager, mediaDataCombineLatest, mediaDataFilter,
activityStarter, Utils.useMediaResumption(context), Utils.useQsMediaPlayer(context))
private val appChangeReceiver = object : BroadcastReceiver() {
@@ -155,12 +171,26 @@
init {
dumpManager.registerDumpable(TAG, this)
+
+ // Initialize the internal processing pipeline. The listeners at the front of the pipeline
+ // are set as internal listeners so that they receive events. From there, events are
+ // propagated through the pipeline. The end of the pipeline is currently mediaDataFilter,
+ // so it is responsible for dispatching events to external listeners. To achieve this,
+ // external listeners that are registered with [MediaDataManager.addListener] are actually
+ // registered as listeners to mediaDataFilter.
+ addInternalListener(mediaTimeoutListener)
+ addInternalListener(mediaResumeListener)
+ addInternalListener(mediaSessionBasedFilter)
+ mediaSessionBasedFilter.addListener(mediaDeviceManager)
+ mediaSessionBasedFilter.addListener(mediaDataCombineLatest)
+ mediaDeviceManager.addListener(mediaDataCombineLatest)
+ mediaDataCombineLatest.addListener(mediaDataFilter)
+
+ // Set up links back into the pipeline for listeners that need to send events upstream.
mediaTimeoutListener.timeoutCallback = { token: String, timedOut: Boolean ->
setTimedOut(token, timedOut) }
- addListener(mediaTimeoutListener)
-
mediaResumeListener.setManager(this)
- addListener(mediaResumeListener)
+ mediaDataFilter.mediaDataManager = this
val suspendFilter = IntentFilter(Intent.ACTION_PACKAGES_SUSPENDED)
broadcastDispatcher.registerReceiver(appChangeReceiver, suspendFilter, null, UserHandle.ALL)
@@ -198,10 +228,9 @@
private fun removeAllForPackage(packageName: String) {
Assert.isMainThread()
- val listenersCopy = listeners.toSet()
val toRemove = mediaEntries.filter { it.value.packageName == packageName }
toRemove.forEach {
- removeEntry(it.key, listenersCopy)
+ removeEntry(it.key)
}
}
@@ -261,12 +290,45 @@
/**
* Add a listener for changes in this class
*/
- fun addListener(listener: Listener) = listeners.add(listener)
+ fun addListener(listener: Listener) {
+ // mediaDataFilter is the current end of the internal pipeline. Register external
+ // listeners as listeners to it.
+ mediaDataFilter.addListener(listener)
+ }
/**
* Remove a listener for changes in this class
*/
- fun removeListener(listener: Listener) = listeners.remove(listener)
+ fun removeListener(listener: Listener) {
+ // Since mediaDataFilter is the current end of the internal pipelie, external listeners
+ // have been registered to it. So, they need to be removed from it too.
+ mediaDataFilter.removeListener(listener)
+ }
+
+ /**
+ * Add a listener for internal events.
+ */
+ private fun addInternalListener(listener: Listener) = internalListeners.add(listener)
+
+ /**
+ * Notify internal listeners of loaded event.
+ *
+ * External listeners registered with [addListener] will be notified after the event propagates
+ * through the internal listener pipeline.
+ */
+ private fun notifyMediaDataLoaded(key: String, oldKey: String?, info: MediaData) {
+ internalListeners.forEach { it.onMediaDataLoaded(key, oldKey, info) }
+ }
+
+ /**
+ * Notify internal listeners of removed event.
+ *
+ * External listeners registered with [addListener] will be notified after the event propagates
+ * through the internal listener pipeline.
+ */
+ private fun notifyMediaDataRemoved(key: String) {
+ internalListeners.forEach { it.onMediaDataRemoved(key) }
+ }
/**
* Called whenever the player has been paused or stopped for a while, or swiped from QQS.
@@ -284,16 +346,13 @@
}
}
- private fun removeEntry(key: String, listenersCopy: Set<Listener>) {
+ private fun removeEntry(key: String) {
mediaEntries.remove(key)
- listenersCopy.forEach {
- it.onMediaDataRemoved(key)
- }
+ notifyMediaDataRemoved(key)
}
fun dismissMediaData(key: String, delay: Long) {
- val listenersCopy = listeners.toSet()
- foregroundExecutor.executeDelayed({ removeEntry(key, listenersCopy) }, delay)
+ foregroundExecutor.executeDelayed({ removeEntry(key) }, delay)
}
private fun loadMediaDataInBgForResumption(
@@ -422,7 +481,7 @@
val runnable = if (action.actionIntent != null) {
Runnable {
if (action.isAuthenticationRequired()) {
- activityStarter.dismissKeyguardThenExecute ({
+ activityStarter.dismissKeyguardThenExecute({
var result = sendPendingIntent(action.actionIntent)
result
}, {}, true)
@@ -550,10 +609,7 @@
if (mediaEntries.containsKey(key)) {
// Otherwise this was removed already
mediaEntries.put(key, data)
- val listenersCopy = listeners.toSet()
- listenersCopy.forEach {
- it.onMediaDataLoaded(key, oldKey, data)
- }
+ notifyMediaDataLoaded(key, oldKey, data)
}
}
@@ -570,31 +626,21 @@
val pkg = removed?.packageName
val migrate = mediaEntries.put(pkg, updated) == null
// Notify listeners of "new" controls when migrating or removed and update when not
- val listenersCopy = listeners.toSet()
if (migrate) {
- listenersCopy.forEach {
- it.onMediaDataLoaded(pkg, key, updated)
- }
+ notifyMediaDataLoaded(pkg, key, updated)
} else {
// Since packageName is used for the key of the resumption controls, it is
// possible that another notification has already been reused for the resumption
// controls of this package. In this case, rather than renaming this player as
// packageName, just remove it and then send a update to the existing resumption
// controls.
- listenersCopy.forEach {
- it.onMediaDataRemoved(key)
- }
- listenersCopy.forEach {
- it.onMediaDataLoaded(pkg, pkg, updated)
- }
+ notifyMediaDataRemoved(key)
+ notifyMediaDataLoaded(pkg, pkg, updated)
}
return
}
if (removed != null) {
- val listenersCopy = listeners.toSet()
- listenersCopy.forEach {
- it.onMediaDataRemoved(key)
- }
+ notifyMediaDataRemoved(key)
}
}
@@ -614,17 +660,31 @@
if (!useMediaResumption) {
// Remove any existing resume controls
- val listenersCopy = listeners.toSet()
val filtered = mediaEntries.filter { !it.value.active }
filtered.forEach {
mediaEntries.remove(it.key)
- listenersCopy.forEach { listener ->
- listener.onMediaDataRemoved(it.key)
- }
+ notifyMediaDataRemoved(it.key)
}
}
}
+ /**
+ * Invoked when the user has dismissed the media carousel
+ */
+ fun onSwipeToDismiss() = mediaDataFilter.onSwipeToDismiss()
+
+ /**
+ * Are there any media notifications active?
+ */
+ fun hasActiveMedia() = mediaDataFilter.hasActiveMedia()
+
+ /**
+ * Are there any media entries we should display?
+ * If resumption is enabled, this will include inactive players
+ * If resumption is disabled, we only want to show active players
+ */
+ fun hasAnyMedia() = mediaDataFilter.hasAnyMedia()
+
interface Listener {
/**
@@ -644,7 +704,8 @@
override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
pw.apply {
- println("listeners: $listeners")
+ println("internalListeners: $internalListeners")
+ println("externalListeners: ${mediaDataFilter.listeners}")
println("mediaEntries: $mediaEntries")
println("useMediaResumption: $useMediaResumption")
println("appsBlockedFromResume: $appsBlockedFromResume")
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
index ae7f66b..102a484 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
@@ -32,26 +32,23 @@
import java.io.PrintWriter
import java.util.concurrent.Executor
import javax.inject.Inject
-import javax.inject.Singleton
/**
* Provides information about the route (ie. device) where playback is occurring.
*/
-@Singleton
class MediaDeviceManager @Inject constructor(
private val context: Context,
private val localMediaManagerFactory: LocalMediaManagerFactory,
private val mr2manager: MediaRouter2Manager,
@Main private val fgExecutor: Executor,
@Background private val bgExecutor: Executor,
- private val mediaDataManager: MediaDataManager,
- private val dumpManager: DumpManager
+ dumpManager: DumpManager
) : MediaDataManager.Listener, Dumpable {
+
private val listeners: MutableSet<Listener> = mutableSetOf()
private val entries: MutableMap<String, Entry> = mutableMapOf()
init {
- mediaDataManager.addListener(this)
dumpManager.registerDumpable(javaClass.name, this)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
index 3598719..ce184aa 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
@@ -14,7 +14,7 @@
class MediaHost @Inject constructor(
private val state: MediaHostStateHolder,
private val mediaHierarchyManager: MediaHierarchyManager,
- private val mediaDataFilter: MediaDataFilter,
+ private val mediaDataManager: MediaDataManager,
private val mediaHostStatesManager: MediaHostStatesManager
) : MediaHostState by state {
lateinit var hostView: UniqueObjectHostView
@@ -79,12 +79,12 @@
// be a delay until the views and the controllers are initialized, leaving us
// with either a blank view or the controllers not yet initialized and the
// measuring wrong
- mediaDataFilter.addListener(listener)
+ mediaDataManager.addListener(listener)
updateViewVisibility()
}
override fun onViewDetachedFromWindow(v: View?) {
- mediaDataFilter.removeListener(listener)
+ mediaDataManager.removeListener(listener)
}
})
@@ -113,9 +113,9 @@
private fun updateViewVisibility() {
visible = if (showsOnlyActiveMedia) {
- mediaDataFilter.hasActiveMedia()
+ mediaDataManager.hasActiveMedia()
} else {
- mediaDataFilter.hasAnyMedia()
+ mediaDataManager.hasAnyMedia()
}
val newVisibility = if (visible) View.VISIBLE else View.GONE
if (newVisibility != hostView.visibility) {
@@ -289,4 +289,4 @@
* Get a copy of this view state, deepcopying all appropriate members
*/
fun copy(): MediaHostState
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt
new file mode 100644
index 0000000..f01713f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2020 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.systemui.media
+
+import android.content.ComponentName
+import android.content.Context
+import android.media.session.MediaController
+import android.media.session.MediaController.PlaybackInfo
+import android.media.session.MediaSession
+import android.media.session.MediaSessionManager
+import android.util.Log
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.statusbar.phone.NotificationListenerWithPlugins
+import java.util.concurrent.Executor
+import javax.inject.Inject
+
+private const val TAG = "MediaSessionBasedFilter"
+
+/**
+ * Filters media loaded events for local media sessions while an app is casting.
+ *
+ * When an app is casting there can be one remote media sessions and potentially more local media
+ * sessions. In this situation, there should only be a media object for the remote session. To
+ * achieve this, update events for the local session need to be filtered.
+ */
+class MediaSessionBasedFilter @Inject constructor(
+ context: Context,
+ private val sessionManager: MediaSessionManager,
+ @Main private val foregroundExecutor: Executor,
+ @Background private val backgroundExecutor: Executor
+) : MediaDataManager.Listener {
+
+ private val listeners: MutableSet<MediaDataManager.Listener> = mutableSetOf()
+
+ // Keep track of MediaControllers for a given package to check if an app is casting and it
+ // filter loaded events for local sessions.
+ private val packageControllers: LinkedHashMap<String, MutableList<MediaController>> =
+ LinkedHashMap()
+
+ // Keep track of the key used for the session tokens. This information is used to know when
+ // dispatch a removed event so that a media object for a local session will be removed.
+ private val keyedTokens: MutableMap<String, MutableList<MediaSession.Token>> = mutableMapOf()
+
+ private val sessionListener = object : MediaSessionManager.OnActiveSessionsChangedListener {
+ override fun onActiveSessionsChanged(controllers: List<MediaController>) {
+ handleControllersChanged(controllers)
+ }
+ }
+
+ init {
+ backgroundExecutor.execute {
+ val name = ComponentName(context, NotificationListenerWithPlugins::class.java)
+ sessionManager.addOnActiveSessionsChangedListener(sessionListener, name)
+ handleControllersChanged(sessionManager.getActiveSessions(name))
+ }
+ }
+
+ /**
+ * Add a listener for filtered [MediaData] changes
+ */
+ fun addListener(listener: MediaDataManager.Listener) = listeners.add(listener)
+
+ /**
+ * Remove a listener that was registered with addListener
+ */
+ fun removeListener(listener: MediaDataManager.Listener) = listeners.remove(listener)
+
+ /**
+ * May filter loaded events by not passing them along to listeners.
+ *
+ * If an app has only one session with playback type PLAYBACK_TYPE_REMOTE, then assuming that
+ * the app is casting. Sometimes apps will send redundant updates to a local session with
+ * playback type PLAYBACK_TYPE_LOCAL. These updates should be filtered to improve the usability
+ * of the media controls.
+ */
+ override fun onMediaDataLoaded(key: String, oldKey: String?, info: MediaData) {
+ backgroundExecutor.execute {
+ val isMigration = oldKey != null && key != oldKey
+ if (isMigration) {
+ keyedTokens.remove(oldKey)?.let { removed -> keyedTokens.put(key, removed) }
+ }
+ if (info.token != null) {
+ keyedTokens.get(key)?.let {
+ tokens ->
+ tokens.add(info.token)
+ } ?: run {
+ val tokens = mutableListOf(info.token)
+ keyedTokens.put(key, tokens)
+ }
+ }
+ // Determine if an app is casting by checking if it has a session with playback type
+ // PLAYBACK_TYPE_REMOTE.
+ val remoteControllers = packageControllers.get(info.packageName)?.filter {
+ it.playbackInfo?.playbackType == PlaybackInfo.PLAYBACK_TYPE_REMOTE
+ }
+ // Limiting search to only apps with a single remote session.
+ val remote = if (remoteControllers?.size == 1) remoteControllers.firstOrNull() else null
+ if (isMigration || remote == null || remote.sessionToken == info.token) {
+ // Not filtering in this case. Passing the event along to listeners.
+ dispatchMediaDataLoaded(key, oldKey, info)
+ } else {
+ // Filtering this event because the app is casting and the loaded events is for a
+ // local session.
+ Log.d(TAG, "filtering key=$key local=${info.token} remote=${remote?.sessionToken}")
+ // If the local session uses a different notification key, then lets go a step
+ // farther and dismiss the media data so that media controls for the local session
+ // don't hang around while casting.
+ if (!keyedTokens.get(key)!!.contains(remote.sessionToken)) {
+ dispatchMediaDataRemoved(key)
+ }
+ }
+ }
+ }
+
+ override fun onMediaDataRemoved(key: String) {
+ // Queue on background thread to ensure ordering of loaded and removed events is maintained.
+ backgroundExecutor.execute {
+ keyedTokens.remove(key)
+ dispatchMediaDataRemoved(key)
+ }
+ }
+
+ private fun dispatchMediaDataLoaded(key: String, oldKey: String?, info: MediaData) {
+ foregroundExecutor.execute {
+ listeners.toSet().forEach { it.onMediaDataLoaded(key, oldKey, info) }
+ }
+ }
+
+ private fun dispatchMediaDataRemoved(key: String) {
+ foregroundExecutor.execute {
+ listeners.toSet().forEach { it.onMediaDataRemoved(key) }
+ }
+ }
+
+ private fun handleControllersChanged(controllers: List<MediaController>) {
+ packageControllers.clear()
+ controllers.forEach {
+ controller ->
+ packageControllers.get(controller.packageName)?.let {
+ tokens ->
+ tokens.add(controller)
+ } ?: run {
+ val tokens = mutableListOf(controller)
+ packageControllers.put(controller.packageName, tokens)
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index ecdd778..d222489 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -87,7 +87,7 @@
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
import com.android.systemui.statusbar.policy.CallbackController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 739d30c..c01bdc4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -60,7 +60,6 @@
import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.LockscreenWallpaper;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ScrimState;
import com.android.systemui.statusbar.phone.StatusBar;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 0445c98..cdf1f10 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -38,7 +38,6 @@
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK
import com.android.systemui.statusbar.phone.DozeParameters
-import com.android.systemui.statusbar.phone.NotificationShadeWindowController
import com.android.systemui.statusbar.phone.PanelExpansionListener
import com.android.systemui.statusbar.phone.ScrimController
import com.android.systemui.statusbar.policy.KeyguardStateController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
new file mode 100644
index 0000000..1fd0b03
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2020 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.systemui.statusbar;
+
+import android.view.ViewGroup;
+
+import androidx.annotation.Nullable;
+
+import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
+
+import java.util.function.Consumer;
+
+/**
+ * Interface to control the state of the notification shade window. Not all methods of this
+ * interface will be used by each implementation of {@link NotificationShadeWindowController}.
+ */
+public interface NotificationShadeWindowController extends RemoteInputController.Callback {
+
+ /**
+ * Registers a {@link StatusBarWindowCallback} to receive notifications about status bar
+ * window state changes.
+ */
+ default void registerCallback(StatusBarWindowCallback callback) {}
+
+ /** Notifies the registered {@link StatusBarWindowCallback} instances. */
+ default void notifyStateChangedCallbacks() {}
+
+ /**
+ * Registers a listener to monitor scrims visibility.
+ *
+ * @param listener A listener to monitor scrims visibility
+ */
+ default void setScrimsVisibilityListener(Consumer<Integer> listener) {}
+
+ /**
+ * Adds the notification shade view to the window manager.
+ */
+ default void attach() {}
+
+ /** Sets the notification shade view. */
+ default void setNotificationShadeView(ViewGroup view) {}
+
+ /** Gets the notification shade view. */
+ @Nullable
+ default ViewGroup getNotificationShadeView() {
+ return null;
+ }
+
+ /** Sets the state of whether the keyguard is currently showing or not. */
+ default void setKeyguardShowing(boolean showing) {}
+
+ /** Sets the state of whether the keyguard is currently occluded or not. */
+ default void setKeyguardOccluded(boolean occluded) {}
+
+ /** Sets the state of whether the keyguard is currently needs input or not. */
+ default void setKeyguardNeedsInput(boolean needsInput) {}
+
+ /** Sets the state of whether the notification shade panel is currently visible or not. */
+ default void setPanelVisible(boolean visible) {}
+
+ /** Sets the state of whether the notification shade is focusable or not. */
+ default void setNotificationShadeFocusable(boolean focusable) {}
+
+ /** Sets the state of whether the bouncer is showing or not. */
+ default void setBouncerShowing(boolean showing) {}
+
+ /** Sets the state of whether the backdrop is showing or not. */
+ default void setBackdropShowing(boolean showing) {}
+
+ /** Sets the state of whether the keyguard is fading away or not. */
+ default void setKeyguardFadingAway(boolean keyguardFadingAway) {}
+
+ /** Sets the state of whether the quick settings is expanded or not. */
+ default void setQsExpanded(boolean expanded) {}
+
+ /** Sets the state of whether the user activities are forced or not. */
+ default void setForceUserActivity(boolean forceUserActivity) {}
+
+ /** Sets the state of whether the user activities are forced or not. */
+ default void setLaunchingActivity(boolean launching) {}
+
+ /** Sets the state of whether the scrim is visible or not. */
+ default void setScrimsVisibility(int scrimsVisibility) {}
+
+ /** Sets the background blur radius of the notification shade window. */
+ default void setBackgroundBlurRadius(int backgroundBlurRadius) {}
+
+ /** Sets the state of whether heads up is showing or not. */
+ default void setHeadsUpShowing(boolean showing) {}
+
+ /** Sets whether the wallpaper supports ambient mode or not. */
+ default void setWallpaperSupportsAmbientMode(boolean supportsAmbientMode) {}
+
+ /** Gets whether the wallpaper is showing or not. */
+ default boolean isShowingWallpaper() {
+ return false;
+ }
+
+ /** Sets whether the window was collapsed by force or not. */
+ default void setForceWindowCollapsed(boolean force) {}
+
+ /** Sets whether panel is expanded or not. */
+ default void setPanelExpanded(boolean isExpanded) {}
+
+ /** Gets whether the panel is expanded or not. */
+ default boolean getPanelExpanded() {
+ return false;
+ }
+
+ /** Sets the state of whether the remote input is active or not. */
+ default void onRemoteInputActive(boolean remoteInputActive) {}
+
+ /** Sets the screen brightness level for when the device is dozing. */
+ default void setDozeScreenBrightness(int value) {}
+
+ /**
+ * Sets whether the screen brightness is forced to the value we use for doze mode by the status
+ * bar window. No-op if the device does not support dozing.
+ */
+ default void setForceDozeBrightness(boolean forceDozeBrightness) {}
+
+ /** Sets the state of whether sysui is dozing or not. */
+ default void setDozing(boolean dozing) {}
+
+ /** Sets the state of whether plugin open is forced or not. */
+ default void setForcePluginOpen(boolean forcePluginOpen) {}
+
+ /** Gets whether we are forcing plugin open or not. */
+ default boolean getForcePluginOpen() {
+ return false;
+ }
+
+ /** Sets the state of whether the notification shade is touchable or not. */
+ default void setNotTouchable(boolean notTouchable) {}
+
+ /** Sets a {@link OtherwisedCollapsedListener}. */
+ default void setStateListener(OtherwisedCollapsedListener listener) {}
+
+ /** Sets a {@link ForcePluginOpenListener}. */
+ default void setForcePluginOpenListener(ForcePluginOpenListener listener) {}
+
+ /** Sets whether the system is in a state where the keyguard is going away. */
+ default void setKeyguardGoingAway(boolean goingAway) {}
+
+ /**
+ * SystemUI may need top-ui to avoid jank when performing animations. After the
+ * animation is performed, the component should remove itself from the list of features that
+ * are forcing SystemUI to be top-ui.
+ */
+ default void setRequestTopUi(boolean requestTopUi, String componentTag) {}
+
+ /**
+ * Custom listener to pipe data back to plugins about whether or not the status bar would be
+ * collapsed if not for the plugin.
+ * TODO: Find cleaner way to do this.
+ */
+ interface OtherwisedCollapsedListener {
+ void setWouldOtherwiseCollapse(boolean otherwiseCollapse);
+ }
+
+ /**
+ * Listener to indicate forcePluginOpen has changed
+ */
+ interface ForcePluginOpenListener {
+ /**
+ * Called when mState.forcePluginOpen is changed
+ */
+ void onChange(boolean forceOpen);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 02a8fee..f272be4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -491,7 +491,6 @@
}
}
- row.showAppOpsIcons(entry.mActiveAppOps);
row.showFeedbackIcon(mAssistantFeedbackController.showFeedbackIndicator(entry));
row.setLastAudiblyAlertedMs(entry.getLastAudiblyAlertedMs());
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index 992f2da..991b79a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -33,6 +33,7 @@
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.NotificationViewHierarchyManager;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
@@ -44,7 +45,6 @@
import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
import com.android.systemui.tracing.ProtoTracer;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java
index 68ec6b6..2f12088 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java
@@ -82,14 +82,9 @@
// extend the lifetime of foreground notification services to show for at least 5 seconds
mNotifPipeline.addNotificationLifetimeExtender(mForegroundLifetimeExtender);
- // listen for new notifications to add appOps
- mNotifPipeline.addCollectionListener(mNotifCollectionListener);
-
// filter out foreground service notifications that aren't necessary anymore
mNotifPipeline.addPreGroupFilter(mNotifFilter);
- // when appOps change, update any relevant notifications to update appOps for
- mAppOpsController.addCallback(ForegroundServiceController.APP_OPS, this::onAppOpsChanged);
}
public NotifSection getSection() {
@@ -186,35 +181,6 @@
};
/**
- * Adds appOps to incoming and updating notifications
- */
- private NotifCollectionListener mNotifCollectionListener = new NotifCollectionListener() {
- @Override
- public void onEntryAdded(NotificationEntry entry) {
- tagAppOps(entry);
- }
-
- @Override
- public void onEntryUpdated(NotificationEntry entry) {
- tagAppOps(entry);
- }
-
- private void tagAppOps(NotificationEntry entry) {
- final StatusBarNotification sbn = entry.getSbn();
- // note: requires that the ForegroundServiceController is updating their appOps first
- ArraySet<Integer> activeOps =
- mForegroundServiceController.getAppOps(
- sbn.getUser().getIdentifier(),
- sbn.getPackageName());
-
- entry.mActiveAppOps.clear();
- if (activeOps != null) {
- entry.mActiveAppOps.addAll(activeOps);
- }
- }
- };
-
- /**
* Puts foreground service notifications into its own section.
*/
private final NotifSection mNotifSection = new NotifSection("ForegroundService") {
@@ -230,53 +196,4 @@
return false;
}
};
-
- private void onAppOpsChanged(int code, int uid, String packageName, boolean active) {
- mMainExecutor.execute(() -> handleAppOpsChanged(code, uid, packageName, active));
- }
-
- /**
- * Update the appOp for the posted notification associated with the current foreground service
- *
- * @param code code for appOp to add/remove
- * @param uid of user the notification is sent to
- * @param packageName package that created the notification
- * @param active whether the appOpCode is active or not
- */
- private void handleAppOpsChanged(int code, int uid, String packageName, boolean active) {
- Assert.isMainThread();
-
- int userId = UserHandle.getUserId(uid);
-
- // Update appOps of the app's posted notifications with standard layouts
- final ArraySet<String> notifKeys =
- mForegroundServiceController.getStandardLayoutKeys(userId, packageName);
- if (notifKeys != null) {
- boolean changed = false;
- for (int i = 0; i < notifKeys.size(); i++) {
- final NotificationEntry entry = findNotificationEntryWithKey(notifKeys.valueAt(i));
- if (entry != null
- && uid == entry.getSbn().getUid()
- && packageName.equals(entry.getSbn().getPackageName())) {
- if (active) {
- changed |= entry.mActiveAppOps.add(code);
- } else {
- changed |= entry.mActiveAppOps.remove(code);
- }
- }
- }
- if (changed) {
- mNotifFilter.invalidateList();
- }
- }
- }
-
- private NotificationEntry findNotificationEntryWithKey(String key) {
- for (NotificationEntry entry : mNotifPipeline.getAllNotifs()) {
- if (entry.getKey().equals(key)) {
- return entry;
- }
- }
- return null;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/AppOpsInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/AppOpsInfo.java
deleted file mode 100644
index 28c53dc..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/AppOpsInfo.java
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.notification.row;
-
-import android.app.AppOpsManager;
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.graphics.drawable.Drawable;
-import android.service.notification.StatusBarNotification;
-import android.util.ArraySet;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.accessibility.AccessibilityEvent;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.UiEventLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.R;
-
-/**
- * The guts of a notification revealed when performing a long press.
- */
-public class AppOpsInfo extends LinearLayout implements NotificationGuts.GutsContent {
- private static final String TAG = "AppOpsGuts";
-
- private PackageManager mPm;
-
- private String mPkg;
- private String mAppName;
- private int mAppUid;
- private StatusBarNotification mSbn;
- private ArraySet<Integer> mAppOps;
- private MetricsLogger mMetricsLogger;
- private OnSettingsClickListener mOnSettingsClickListener;
- private NotificationGuts mGutsContainer;
- private UiEventLogger mUiEventLogger;
-
- private OnClickListener mOnOk = v -> {
- mGutsContainer.closeControls(v, false);
- };
-
- public AppOpsInfo(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public interface OnSettingsClickListener {
- void onClick(View v, String pkg, int uid, ArraySet<Integer> ops);
- }
-
- public void bindGuts(final PackageManager pm,
- final OnSettingsClickListener onSettingsClick,
- final StatusBarNotification sbn,
- final UiEventLogger uiEventLogger,
- ArraySet<Integer> activeOps) {
- mPkg = sbn.getPackageName();
- mSbn = sbn;
- mPm = pm;
- mAppName = mPkg;
- mOnSettingsClickListener = onSettingsClick;
- mAppOps = activeOps;
- mUiEventLogger = uiEventLogger;
-
- bindHeader();
- bindPrompt();
- bindButtons();
-
- logUiEvent(NotificationAppOpsEvent.NOTIFICATION_APP_OPS_OPEN);
- mMetricsLogger = new MetricsLogger();
- mMetricsLogger.visibility(MetricsEvent.APP_OPS_GUTS, true);
- }
-
- private void bindHeader() {
- // Package name
- Drawable pkgicon = null;
- ApplicationInfo info;
- try {
- info = mPm.getApplicationInfo(mPkg,
- PackageManager.MATCH_UNINSTALLED_PACKAGES
- | PackageManager.MATCH_DISABLED_COMPONENTS
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
- | PackageManager.MATCH_DIRECT_BOOT_AWARE);
- if (info != null) {
- mAppUid = mSbn.getUid();
- mAppName = String.valueOf(mPm.getApplicationLabel(info));
- pkgicon = mPm.getApplicationIcon(info);
- }
- } catch (PackageManager.NameNotFoundException e) {
- // app is gone, just show package name and generic icon
- pkgicon = mPm.getDefaultActivityIcon();
- }
- ((ImageView) findViewById(R.id.pkgicon)).setImageDrawable(pkgicon);
- ((TextView) findViewById(R.id.pkgname)).setText(mAppName);
- }
-
- private void bindPrompt() {
- final TextView prompt = findViewById(R.id.prompt);
- prompt.setText(getPrompt());
- }
-
- private void bindButtons() {
- View settings = findViewById(R.id.settings);
- settings.setOnClickListener((View view) -> {
- mOnSettingsClickListener.onClick(view, mPkg, mAppUid, mAppOps);
- });
- TextView ok = findViewById(R.id.ok);
- ok.setOnClickListener(mOnOk);
- ok.setAccessibilityDelegate(mGutsContainer.getAccessibilityDelegate());
- }
-
- private String getPrompt() {
- if (mAppOps == null || mAppOps.size() == 0) {
- return "";
- } else if (mAppOps.size() == 1) {
- if (mAppOps.contains(AppOpsManager.OP_CAMERA)) {
- return mContext.getString(R.string.appops_camera);
- } else if (mAppOps.contains(AppOpsManager.OP_RECORD_AUDIO)) {
- return mContext.getString(R.string.appops_microphone);
- } else {
- return mContext.getString(R.string.appops_overlay);
- }
- } else if (mAppOps.size() == 2) {
- if (mAppOps.contains(AppOpsManager.OP_CAMERA)) {
- if (mAppOps.contains(AppOpsManager.OP_RECORD_AUDIO)) {
- return mContext.getString(R.string.appops_camera_mic);
- } else {
- return mContext.getString(R.string.appops_camera_overlay);
- }
- } else {
- return mContext.getString(R.string.appops_mic_overlay);
- }
- } else {
- return mContext.getString(R.string.appops_camera_mic_overlay);
- }
- }
-
- @Override
- public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
- super.onInitializeAccessibilityEvent(event);
- if (mGutsContainer != null &&
- event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
- if (mGutsContainer.isExposed()) {
- event.getText().add(mContext.getString(
- R.string.notification_channel_controls_opened_accessibility, mAppName));
- } else {
- event.getText().add(mContext.getString(
- R.string.notification_channel_controls_closed_accessibility, mAppName));
- }
- }
- }
-
- @Override
- public void setGutsParent(NotificationGuts guts) {
- mGutsContainer = guts;
- }
-
- @Override
- public boolean willBeRemoved() {
- return false;
- }
-
- @Override
- public boolean shouldBeSaved() {
- return false;
- }
-
- @Override
- public boolean needsFalsingProtection() {
- return false;
- }
-
- @Override
- public View getContentView() {
- return this;
- }
-
- @Override
- public boolean handleCloseControls(boolean save, boolean force) {
- logUiEvent(NotificationAppOpsEvent.NOTIFICATION_APP_OPS_CLOSE);
- if (mMetricsLogger != null) {
- mMetricsLogger.visibility(MetricsEvent.APP_OPS_GUTS, false);
- }
- return false;
- }
-
- @Override
- public int getActualHeight() {
- return getHeight();
- }
-
- private void logUiEvent(NotificationAppOpsEvent event) {
- if (mSbn != null) {
- mUiEventLogger.logWithInstanceId(event,
- mSbn.getUid(), mSbn.getPackageName(), mSbn.getInstanceId());
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 22d0357..d7fa54f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -239,7 +239,7 @@
private boolean mShowNoBackground;
private ExpandableNotificationRow mNotificationParent;
private OnExpandClickListener mOnExpandClickListener;
- private View.OnClickListener mOnAppOpsClickListener;
+ private View.OnClickListener mOnAppClickListener;
private View.OnClickListener mOnFeedbackClickListener;
// Listener will be called when receiving a long click event.
@@ -1139,7 +1139,6 @@
items.add(NotificationMenuRow.createPartialConversationItem(mContext));
items.add(NotificationMenuRow.createInfoItem(mContext));
items.add(NotificationMenuRow.createSnoozeItem(mContext));
- items.add(NotificationMenuRow.createAppOpsItem(mContext));
mMenuRow.setMenuItems(items);
}
if (existed) {
@@ -1598,7 +1597,6 @@
RowContentBindStage rowContentBindStage,
OnExpandClickListener onExpandClickListener,
NotificationMediaManager notificationMediaManager,
- CoordinateOnClickListener onAppOpsClickListener,
CoordinateOnClickListener onFeedbackClickListener,
FalsingManager falsingManager,
StatusBarStateController statusBarStateController,
@@ -1619,7 +1617,6 @@
mRowContentBindStage = rowContentBindStage;
mOnExpandClickListener = onExpandClickListener;
mMediaManager = notificationMediaManager;
- setAppOpsOnClickListener(onAppOpsClickListener);
setOnFeedbackClickListener(onFeedbackClickListener);
mFalsingManager = falsingManager;
mStatusbarStateController = statusBarStateController;
@@ -1677,14 +1674,6 @@
requestLayout();
}
- public void showAppOpsIcons(ArraySet<Integer> activeOps) {
- if (mIsSummaryWithChildren) {
- mChildrenContainer.showAppOpsIcons(activeOps);
- }
- mPrivateLayout.showAppOpsIcons(activeOps);
- mPublicLayout.showAppOpsIcons(activeOps);
- }
-
public void showFeedbackIcon(boolean show) {
if (mIsSummaryWithChildren) {
mChildrenContainer.showFeedbackIcon(show);
@@ -1721,24 +1710,6 @@
mPublicLayout.setRecentlyAudiblyAlerted(audiblyAlertedRecently);
}
- public View.OnClickListener getAppOpsOnClickListener() {
- return mOnAppOpsClickListener;
- }
-
- void setAppOpsOnClickListener(CoordinateOnClickListener l) {
- mOnAppOpsClickListener = v -> {
- createMenu();
- NotificationMenuRowPlugin provider = getProvider();
- if (provider == null) {
- return;
- }
- MenuItem menuItem = provider.getAppOpsMenuItem(mContext);
- if (menuItem != null) {
- l.onClick(this, v.getWidth() / 2, v.getHeight() / 2, menuItem);
- }
- };
- }
-
public View.OnClickListener getFeedbackOnClickListener() {
return mOnFeedbackClickListener;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index 1dbec6603..690dce9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -69,7 +69,6 @@
private final ExpandableNotificationRow.ExpansionLogger mExpansionLogger =
this::logNotificationExpansion;
- private final ExpandableNotificationRow.CoordinateOnClickListener mOnAppOpsClickListener;
private final ExpandableNotificationRow.CoordinateOnClickListener mOnFeedbackClickListener;
private final NotificationGutsManager mNotificationGutsManager;
private final OnDismissCallback mOnDismissCallback;
@@ -110,7 +109,6 @@
mStatusBarStateController = statusBarStateController;
mNotificationGutsManager = notificationGutsManager;
mOnDismissCallback = onDismissCallback;
- mOnAppOpsClickListener = mNotificationGutsManager::openGuts;
mOnFeedbackClickListener = mNotificationGutsManager::openGuts;
mAllowLongPress = allowLongPress;
mFalsingManager = falsingManager;
@@ -132,7 +130,6 @@
mRowContentBindStage,
mOnExpandClickListener,
mMediaManager,
- mOnAppOpsClickListener,
mOnFeedbackClickListener,
mFalsingManager,
mStatusBarStateController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 2986b9b..c7e44c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -1568,18 +1568,6 @@
return header;
}
- public void showAppOpsIcons(ArraySet<Integer> activeOps) {
- if (mContractedChild != null) {
- mContractedWrapper.showAppOpsIcons(activeOps);
- }
- if (mExpandedChild != null) {
- mExpandedWrapper.showAppOpsIcons(activeOps);
- }
- if (mHeadsUpChild != null) {
- mHeadsUpWrapper.showAppOpsIcons(activeOps);
- }
- }
-
public void showFeedbackIcon(boolean show) {
if (mContractedChild != null) {
mContractedWrapper.showFeedbackIcon(show);
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 729b131..05d44f6 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
@@ -268,8 +268,6 @@
try {
if (gutsView instanceof NotificationSnooze) {
initializeSnoozeView(row, (NotificationSnooze) gutsView);
- } else if (gutsView instanceof AppOpsInfo) {
- initializeAppOpsInfo(row, (AppOpsInfo) gutsView);
} else if (gutsView instanceof NotificationInfo) {
initializeNotificationInfo(row, (NotificationInfo) gutsView);
} else if (gutsView instanceof NotificationConversationInfo) {
@@ -309,36 +307,6 @@
}
/**
- * Sets up the {@link AppOpsInfo} inside the notification row's guts.
- *
- * @param row view to set up the guts for
- * @param appOpsInfoView view to set up/bind within {@code row}
- */
- private void initializeAppOpsInfo(
- final ExpandableNotificationRow row,
- AppOpsInfo appOpsInfoView) {
- NotificationGuts guts = row.getGuts();
- StatusBarNotification sbn = row.getEntry().getSbn();
- UserHandle userHandle = sbn.getUser();
- PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
- userHandle.getIdentifier());
-
- AppOpsInfo.OnSettingsClickListener onSettingsClick =
- (View v, String pkg, int uid, ArraySet<Integer> ops) -> {
- mUiEventLogger.logWithInstanceId(
- NotificationAppOpsEvent.NOTIFICATION_APP_OPS_SETTINGS_CLICK,
- sbn.getUid(), sbn.getPackageName(), sbn.getInstanceId());
- mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_OPS_GUTS_SETTINGS);
- guts.resetFalsingCheck();
- startAppOpsSettingsActivity(pkg, uid, ops, row);
- };
- if (!row.getEntry().mActiveAppOps.isEmpty()) {
- appOpsInfoView.bindGuts(pmUser, onSettingsClick, sbn, mUiEventLogger,
- row.getEntry().mActiveAppOps);
- }
- }
-
- /**
* Sets up the {@link FeedbackInfo} inside the notification row's guts.
*
* @param row view to set up the guts for
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index d264af9..205cecc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -76,7 +76,6 @@
private Context mContext;
private FrameLayout mMenuContainer;
private NotificationMenuItem mInfoItem;
- private MenuItem mAppOpsItem;
private MenuItem mFeedbackItem;
private MenuItem mSnoozeItem;
private ArrayList<MenuItem> mLeftMenuItems;
@@ -138,11 +137,6 @@
}
@Override
- public MenuItem getAppOpsMenuItem(Context context) {
- return mAppOpsItem;
- }
-
- @Override
public MenuItem getFeedbackMenuItem(Context context) {
return mFeedbackItem;
}
@@ -264,7 +258,6 @@
// Only show snooze for non-foreground notifications, and if the setting is on
mSnoozeItem = createSnoozeItem(mContext);
}
- mAppOpsItem = createAppOpsItem(mContext);
mFeedbackItem = createFeedbackItem(mContext);
NotificationEntry entry = mParent.getEntry();
int personNotifType = mPeopleNotificationIdentifier
@@ -281,7 +274,6 @@
mRightMenuItems.add(mSnoozeItem);
}
mRightMenuItems.add(mInfoItem);
- mRightMenuItems.add(mAppOpsItem);
mRightMenuItems.add(mFeedbackItem);
mLeftMenuItems.addAll(mRightMenuItems);
@@ -690,14 +682,6 @@
R.drawable.ic_settings);
}
- static MenuItem createAppOpsItem(Context context) {
- AppOpsInfo appOpsContent = (AppOpsInfo) LayoutInflater.from(context).inflate(
- R.layout.app_ops_info, null, false);
- MenuItem info = new NotificationMenuItem(context, null, appOpsContent,
- -1 /*don't show in slow swipe menu */);
- return info;
- }
-
static MenuItem createFeedbackItem(Context context) {
FeedbackInfo feedbackContent = (FeedbackInfo) LayoutInflater.from(context).inflate(
R.layout.feedback_info, null, false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index 4651771..3f58674 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -18,7 +18,6 @@
import static com.android.systemui.statusbar.notification.TransformState.TRANSFORM_Y;
-import android.app.AppOpsManager;
import android.app.Notification;
import android.content.Context;
import android.util.ArraySet;
@@ -64,10 +63,6 @@
private TextView mHeaderText;
private TextView mAppNameText;
private ImageView mWorkProfileImage;
- private View mCameraIcon;
- private View mMicIcon;
- private View mOverlayIcon;
- private View mAppOps;
private View mAudiblyAlertedIcon;
private FrameLayout mIconContainer;
private View mFeedbackIcon;
@@ -109,7 +104,6 @@
}
}, TRANSFORMING_VIEW_TITLE);
resolveHeaderViews();
- addAppOpsOnClickListener(row);
addFeedbackOnClickListener(row);
}
@@ -121,10 +115,6 @@
mExpandButton = mView.findViewById(com.android.internal.R.id.expand_button);
mWorkProfileImage = mView.findViewById(com.android.internal.R.id.profile_badge);
mNotificationHeader = mView.findViewById(com.android.internal.R.id.notification_header);
- mCameraIcon = mView.findViewById(com.android.internal.R.id.camera);
- mMicIcon = mView.findViewById(com.android.internal.R.id.mic);
- mOverlayIcon = mView.findViewById(com.android.internal.R.id.overlay);
- mAppOps = mView.findViewById(com.android.internal.R.id.app_ops);
mAudiblyAlertedIcon = mView.findViewById(com.android.internal.R.id.alerted_icon);
mFeedbackIcon = mView.findViewById(com.android.internal.R.id.feedback);
if (mNotificationHeader != null) {
@@ -133,38 +123,6 @@
}
}
- private void addAppOpsOnClickListener(ExpandableNotificationRow row) {
- View.OnClickListener listener = row.getAppOpsOnClickListener();
- if (mNotificationHeader != null) {
- mNotificationHeader.setAppOpsOnClickListener(listener);
- }
- if (mAppOps != null) {
- mAppOps.setOnClickListener(listener);
- }
- }
-
- /**
- * Shows or hides 'app op in use' icons based on app usage.
- */
- @Override
- public void showAppOpsIcons(ArraySet<Integer> appOps) {
- if (appOps == null) {
- return;
- }
- if (mOverlayIcon != null) {
- mOverlayIcon.setVisibility(appOps.contains(AppOpsManager.OP_SYSTEM_ALERT_WINDOW)
- ? View.VISIBLE : View.GONE);
- }
- if (mCameraIcon != null) {
- mCameraIcon.setVisibility(appOps.contains(AppOpsManager.OP_CAMERA)
- ? View.VISIBLE : View.GONE);
- }
- if (mMicIcon != null) {
- mMicIcon.setVisibility(appOps.contains(AppOpsManager.OP_RECORD_AUDIO)
- ? View.VISIBLE : View.GONE);
- }
- }
-
private void addFeedbackOnClickListener(ExpandableNotificationRow row) {
View.OnClickListener listener = row.getFeedbackOnClickListener();
if (mNotificationHeader != null) {
@@ -304,15 +262,6 @@
mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TITLE,
mHeaderText);
}
- if (mCameraIcon != null) {
- mTransformationHelper.addViewTransformingToSimilar(mCameraIcon);
- }
- if (mMicIcon != null) {
- mTransformationHelper.addViewTransformingToSimilar(mMicIcon);
- }
- if (mOverlayIcon != null) {
- mTransformationHelper.addViewTransformingToSimilar(mOverlayIcon);
- }
if (mAudiblyAlertedIcon != null) {
mTransformationHelper.addViewTransformingToSimilar(mAudiblyAlertedIcon);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
index 93d3f3b..33c93905 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
@@ -22,14 +22,12 @@
import android.app.Notification;
import android.content.Context;
import android.content.res.ColorStateList;
-import android.graphics.drawable.Drawable;
import android.media.MediaMetadata;
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
import android.metrics.LogMaker;
import android.os.Handler;
-import android.service.notification.StatusBarNotification;
import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.View;
@@ -43,13 +41,9 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.widget.MediaNotificationView;
import com.android.systemui.Dependency;
-import com.android.systemui.qs.QSPanel;
-import com.android.systemui.qs.QuickQSPanel;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.TransformableView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
-import com.android.systemui.util.Utils;
import java.util.Timer;
import java.util.TimerTask;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index 605fbc0..42f5e38 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -97,14 +97,6 @@
}
/**
- * Show a set of app opp icons in the layout.
- *
- * @param appOps which app ops to show
- */
- public void showAppOpsIcons(ArraySet<Integer> appOps) {
- }
-
- /**
* Shows or hides feedback icon.
*/
public void showFeedbackIcon(boolean show) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 93c2377..7e4266c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -1303,20 +1303,6 @@
}
/**
- * Show a set of app opp icons in the layout.
- *
- * @param appOps which app ops to show
- */
- public void showAppOpsIcons(ArraySet<Integer> appOps) {
- if (mNotificationHeaderWrapper != null) {
- mNotificationHeaderWrapper.showAppOpsIcons(appOps);
- }
- if (mNotificationHeaderWrapperLowPriority != null) {
- mNotificationHeaderWrapperLowPriority.showAppOpsIcons(appOps);
- }
- }
-
- /**
* Shows or hides feedback icon.
*/
public void showFeedbackIcon(boolean show) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 0e76c904..9d920c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -47,6 +47,7 @@
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import java.io.FileDescriptor;
@@ -157,11 +158,11 @@
private DozeScrimController mDozeScrimController;
private KeyguardViewMediator mKeyguardViewMediator;
private ScrimController mScrimController;
- private StatusBar mStatusBar;
private PendingAuthenticated mPendingAuthenticated = null;
private boolean mPendingShowBouncer;
private boolean mHasScreenTurnedOnSinceAuthenticating;
private boolean mFadedAwayAfterWakeAndUnlock;
+ private BiometricModeListener mBiometricModeListener;
private final MetricsLogger mMetricsLogger;
@@ -243,7 +244,7 @@
@Inject
public BiometricUnlockController(Context context, DozeScrimController dozeScrimController,
KeyguardViewMediator keyguardViewMediator, ScrimController scrimController,
- StatusBar statusBar, ShadeController shadeController,
+ ShadeController shadeController,
NotificationShadeWindowController notificationShadeWindowController,
KeyguardStateController keyguardStateController, Handler handler,
KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -264,7 +265,6 @@
mDozeScrimController = dozeScrimController;
mKeyguardViewMediator = keyguardViewMediator;
mScrimController = scrimController;
- mStatusBar = statusBar;
mKeyguardStateController = keyguardStateController;
mHandler = handler;
mWakeUpDelay = resources.getInteger(com.android.internal.R.integer.config_wakeUpDelayDoze);
@@ -278,6 +278,11 @@
mKeyguardViewController = keyguardViewController;
}
+ /** Sets a {@link BiometricModeListener}. */
+ public void setBiometricModeListener(BiometricModeListener biometricModeListener) {
+ mBiometricModeListener = biometricModeListener;
+ }
+
private final Runnable mReleaseBiometricWakeLockRunnable = new Runnable() {
@Override
public void run() {
@@ -434,19 +439,25 @@
} else {
mKeyguardViewMediator.onWakeAndUnlocking();
}
- if (mStatusBar.getNavigationBarView() != null) {
- mStatusBar.getNavigationBarView().setWakeAndUnlocking(true);
- }
Trace.endSection();
break;
case MODE_ONLY_WAKE:
case MODE_NONE:
break;
}
- mStatusBar.notifyBiometricAuthModeChanged();
+ onModeChanged(mMode);
+ if (mBiometricModeListener != null) {
+ mBiometricModeListener.notifyBiometricAuthModeChanged();
+ }
Trace.endSection();
}
+ private void onModeChanged(@WakeAndUnlockMode int mode) {
+ if (mBiometricModeListener != null) {
+ mBiometricModeListener.onModeChanged(mode);
+ }
+ }
+
private void showBouncer() {
if (mMode == MODE_SHOW_BOUNCER) {
mKeyguardViewController.showBouncer(false);
@@ -619,10 +630,10 @@
mMode = MODE_NONE;
mBiometricType = null;
mNotificationShadeWindowController.setForceDozeBrightness(false);
- if (mStatusBar.getNavigationBarView() != null) {
- mStatusBar.getNavigationBarView().setWakeAndUnlocking(false);
+ if (mBiometricModeListener != null) {
+ mBiometricModeListener.onResetMode();
+ mBiometricModeListener.notifyBiometricAuthModeChanged();
}
- mStatusBar.notifyBiometricAuthModeChanged();
}
@VisibleForTesting
@@ -702,4 +713,14 @@
return 3;
}
}
+
+ /** An interface to interact with the {@link BiometricUnlockController}. */
+ public interface BiometricModeListener {
+ /** Called when {@code mMode} is reset to {@link #MODE_NONE}. */
+ void onResetMode();
+ /** Called when {@code mMode} has changed in {@link #startWakeAndUnlock(int)}. */
+ void onModeChanged(@WakeAndUnlockMode int mode);
+ /** Called after processing {@link #onModeChanged(int)}. */
+ void notifyBiometricAuthModeChanged();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index 4afeba8..de74d4e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -36,6 +36,7 @@
import com.android.systemui.doze.DozeReceiver;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
index bc73be1..07b0a4b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
@@ -47,7 +47,7 @@
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.statusbar.RemoteInputController.Callback;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -72,8 +72,8 @@
* Encapsulates all logic for the notification shade window state management.
*/
@Singleton
-public class NotificationShadeWindowController implements Callback, Dumpable,
- ConfigurationListener {
+public class NotificationShadeWindowControllerImpl implements NotificationShadeWindowController,
+ Dumpable, ConfigurationListener {
private static final String TAG = "NotificationShadeWindowController";
private static final boolean DEBUG = false;
@@ -103,7 +103,7 @@
private final SysuiColorExtractor mColorExtractor;
@Inject
- public NotificationShadeWindowController(Context context, WindowManager windowManager,
+ public NotificationShadeWindowControllerImpl(Context context, WindowManager windowManager,
IActivityManager activityManager, DozeParameters dozeParameters,
StatusBarStateController statusBarStateController,
ConfigurationController configurationController,
@@ -147,6 +147,7 @@
/**
* Register to receive notifications about status bar window state changes.
*/
+ @Override
public void registerCallback(StatusBarWindowCallback callback) {
// Prevent adding duplicate callbacks
for (int i = 0; i < mCallbacks.size(); i++) {
@@ -161,6 +162,7 @@
* Register a listener to monitor scrims visibility
* @param listener A listener to monitor scrims visibility
*/
+ @Override
public void setScrimsVisibilityListener(Consumer<Integer> listener) {
if (listener != null && mScrimsVisibilityListener != listener) {
mScrimsVisibilityListener = listener;
@@ -176,6 +178,7 @@
/**
* Adds the notification shade view to the window manager.
*/
+ @Override
public void attach() {
// Now that the notification shade encompasses the sliding panel and its
// translucent backdrop, the entire thing is made TRANSLUCENT and is
@@ -214,14 +217,17 @@
}
}
+ @Override
public void setNotificationShadeView(ViewGroup view) {
mNotificationShadeView = view;
}
+ @Override
public ViewGroup getNotificationShadeView() {
return mNotificationShadeView;
}
+ @Override
public void setDozeScreenBrightness(int value) {
mScreenBrightnessDoze = value / 255f;
}
@@ -406,6 +412,7 @@
notifyStateChangedCallbacks();
}
+ @Override
public void notifyStateChangedCallbacks() {
for (int i = 0; i < mCallbacks.size(); i++) {
StatusBarWindowCallback cb = mCallbacks.get(i).get();
@@ -445,62 +452,74 @@
}
}
+ @Override
public void setKeyguardShowing(boolean showing) {
mCurrentState.mKeyguardShowing = showing;
apply(mCurrentState);
}
+ @Override
public void setKeyguardOccluded(boolean occluded) {
mCurrentState.mKeyguardOccluded = occluded;
apply(mCurrentState);
}
+ @Override
public void setKeyguardNeedsInput(boolean needsInput) {
mCurrentState.mKeyguardNeedsInput = needsInput;
apply(mCurrentState);
}
+ @Override
public void setPanelVisible(boolean visible) {
mCurrentState.mPanelVisible = visible;
mCurrentState.mNotificationShadeFocusable = visible;
apply(mCurrentState);
}
+ @Override
public void setNotificationShadeFocusable(boolean focusable) {
mCurrentState.mNotificationShadeFocusable = focusable;
apply(mCurrentState);
}
+ @Override
public void setBouncerShowing(boolean showing) {
mCurrentState.mBouncerShowing = showing;
apply(mCurrentState);
}
+ @Override
public void setBackdropShowing(boolean showing) {
mCurrentState.mBackdropShowing = showing;
apply(mCurrentState);
}
+ @Override
public void setKeyguardFadingAway(boolean keyguardFadingAway) {
mCurrentState.mKeyguardFadingAway = keyguardFadingAway;
apply(mCurrentState);
}
+ @Override
public void setQsExpanded(boolean expanded) {
mCurrentState.mQsExpanded = expanded;
apply(mCurrentState);
}
+ @Override
public void setForceUserActivity(boolean forceUserActivity) {
mCurrentState.mForceUserActivity = forceUserActivity;
apply(mCurrentState);
}
- void setLaunchingActivity(boolean launching) {
+ @Override
+ public void setLaunchingActivity(boolean launching) {
mCurrentState.mLaunchingActivity = launching;
apply(mCurrentState);
}
+ @Override
public void setScrimsVisibility(int scrimsVisibility) {
mCurrentState.mScrimsVisibility = scrimsVisibility;
apply(mCurrentState);
@@ -512,6 +531,7 @@
* {@link com.android.systemui.statusbar.NotificationShadeDepthController}.
* @param backgroundBlurRadius Radius in pixels.
*/
+ @Override
public void setBackgroundBlurRadius(int backgroundBlurRadius) {
if (mCurrentState.mBackgroundBlurRadius == backgroundBlurRadius) {
return;
@@ -520,11 +540,13 @@
apply(mCurrentState);
}
+ @Override
public void setHeadsUpShowing(boolean showing) {
mCurrentState.mHeadsUpShowing = showing;
apply(mCurrentState);
}
+ @Override
public void setWallpaperSupportsAmbientMode(boolean supportsAmbientMode) {
mCurrentState.mWallpaperSupportsAmbientMode = supportsAmbientMode;
apply(mCurrentState);
@@ -543,11 +565,13 @@
* Used for when a heads-up comes in but we still need to wait for the touchable regions to
* be computed.
*/
+ @Override
public void setForceWindowCollapsed(boolean force) {
mCurrentState.mForceCollapsed = force;
apply(mCurrentState);
}
+ @Override
public void setPanelExpanded(boolean isExpanded) {
mCurrentState.mPanelExpanded = isExpanded;
apply(mCurrentState);
@@ -563,16 +587,19 @@
* Set whether the screen brightness is forced to the value we use for doze mode by the status
* bar window.
*/
+ @Override
public void setForceDozeBrightness(boolean forceDozeBrightness) {
mCurrentState.mForceDozeBrightness = forceDozeBrightness;
apply(mCurrentState);
}
+ @Override
public void setDozing(boolean dozing) {
mCurrentState.mDozing = dozing;
apply(mCurrentState);
}
+ @Override
public void setForcePluginOpen(boolean forcePluginOpen) {
mCurrentState.mForcePluginOpen = forcePluginOpen;
apply(mCurrentState);
@@ -584,10 +611,12 @@
/**
* The forcePluginOpen state for the status bar.
*/
+ @Override
public boolean getForcePluginOpen() {
return mCurrentState.mForcePluginOpen;
}
+ @Override
public void setNotTouchable(boolean notTouchable) {
mCurrentState.mNotTouchable = notTouchable;
apply(mCurrentState);
@@ -596,24 +625,29 @@
/**
* Whether the status bar panel is expanded or not.
*/
+ @Override
public boolean getPanelExpanded() {
return mCurrentState.mPanelExpanded;
}
+ @Override
public void setStateListener(OtherwisedCollapsedListener listener) {
mListener = listener;
}
+ @Override
public void setForcePluginOpenListener(ForcePluginOpenListener listener) {
mForcePluginOpenListener = listener;
}
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println(TAG + ":");
pw.println(" mKeyguardDisplayMode=" + mKeyguardDisplayMode);
pw.println(mCurrentState);
}
+ @Override
public boolean isShowingWallpaper() {
return !mCurrentState.mBackdropShowing;
}
@@ -632,6 +666,7 @@
/**
* When keyguard will be dismissed but didn't start animation yet.
*/
+ @Override
public void setKeyguardGoingAway(boolean goingAway) {
mCurrentState.mKeyguardGoingAway = goingAway;
apply(mCurrentState);
@@ -642,6 +677,7 @@
* animation is performed, the component should remove itself from the list of features that
* are forcing SystemUI to be top-ui.
*/
+ @Override
public void setRequestTopUi(boolean requestTopUi, String componentTag) {
if (requestTopUi) {
mCurrentState.mComponentsForcingTopUi.add(componentTag);
@@ -725,23 +761,4 @@
setDozing(isDozing);
}
};
-
- /**
- * Custom listener to pipe data back to plugins about whether or not the status bar would be
- * collapsed if not for the plugin.
- * TODO: Find cleaner way to do this.
- */
- public interface OtherwisedCollapsedListener {
- void setWouldOtherwiseCollapse(boolean otherwiseCollapse);
- }
-
- /**
- * Listener to indicate forcePluginOpen has changed
- */
- public interface ForcePluginOpenListener {
- /**
- * Called when mState.forcePluginOpen is changed
- */
- void onChange(boolean forceOpen);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
index 42222d7..53cc267 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
@@ -44,6 +44,7 @@
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationShadeDepthController;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.SuperStatusBarViewFactory;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
index 3330615..921bd4f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
@@ -26,6 +26,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.StatusBarState;
import java.util.ArrayList;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 4fbd49a..5b251be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -191,6 +191,7 @@
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShadeDepthController;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.NotificationShelfController;
import com.android.systemui.statusbar.NotificationViewHierarchyManager;
import com.android.systemui.statusbar.PulseExpansionHandler;
@@ -1473,6 +1474,34 @@
protected void startKeyguard() {
Trace.beginSection("StatusBar#startKeyguard");
mBiometricUnlockController = mBiometricUnlockControllerLazy.get();
+ mBiometricUnlockController.setBiometricModeListener(
+ new BiometricUnlockController.BiometricModeListener() {
+ @Override
+ public void onResetMode() {
+ setWakeAndUnlocking(false);
+ }
+
+ @Override
+ public void onModeChanged(int mode) {
+ switch (mode) {
+ case BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM:
+ case BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING:
+ case BiometricUnlockController.MODE_WAKE_AND_UNLOCK:
+ setWakeAndUnlocking(true);
+ }
+ }
+
+ @Override
+ public void notifyBiometricAuthModeChanged() {
+ StatusBar.this.notifyBiometricAuthModeChanged();
+ }
+
+ private void setWakeAndUnlocking(boolean wakeAndUnlocking) {
+ if (getNavigationBarView() != null) {
+ getNavigationBarView().setWakeAndUnlocking(wakeAndUnlocking);
+ }
+ }
+ });
mStatusBarKeyguardViewManager.registerStatusBar(
/* statusBar= */ this, getBouncerContainer(),
mNotificationPanelViewController, mBiometricUnlockController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index f3a7c50..e159fad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -56,6 +56,7 @@
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index f1715be..0ec22b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -52,6 +52,7 @@
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.NotificationViewHierarchyManager;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
index 9c7f490..b79bb70 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
@@ -32,6 +32,7 @@
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.ScreenDecorations;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index dd4af54..d230eca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -53,6 +53,7 @@
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShadeDepthController;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.NotificationViewHierarchyManager;
import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.SuperStatusBarViewFactory;
@@ -80,7 +81,6 @@
import com.android.systemui.statusbar.phone.LockscreenLockIconController;
import com.android.systemui.statusbar.phone.LockscreenWallpaper;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index 5257ce4..4ae9665 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -84,7 +84,7 @@
R.bool.config_showWifiIndicatorWhenEnabled);
boolean wifiVisible = mCurrentState.enabled && (
(mCurrentState.connected && mCurrentState.inetCondition == 1)
- || !mHasMobileDataFeature || mWifiTracker.isDefaultNetwork
+ || !mHasMobileDataFeature || mCurrentState.isDefault
|| visibleWhenEnabled);
String wifiDesc = mCurrentState.connected ? mCurrentState.ssid : null;
boolean ssidPresent = wifiVisible && mCurrentState.ssid != null;
@@ -107,6 +107,7 @@
public void fetchInitialState() {
mWifiTracker.fetchInitialState();
mCurrentState.enabled = mWifiTracker.enabled;
+ mCurrentState.isDefault = mWifiTracker.isDefaultNetwork;
mCurrentState.connected = mWifiTracker.connected;
mCurrentState.ssid = mWifiTracker.ssid;
mCurrentState.rssi = mWifiTracker.rssi;
@@ -121,6 +122,7 @@
public void handleBroadcast(Intent intent) {
mWifiTracker.handleBroadcast(intent);
mCurrentState.enabled = mWifiTracker.enabled;
+ mCurrentState.isDefault = mWifiTracker.isDefaultNetwork;
mCurrentState.connected = mWifiTracker.connected;
mCurrentState.ssid = mWifiTracker.ssid;
mCurrentState.rssi = mWifiTracker.rssi;
@@ -131,6 +133,7 @@
private void handleStatusUpdated() {
mCurrentState.statusLabel = mWifiTracker.statusLabel;
+ mCurrentState.isDefault = mWifiTracker.isDefaultNetwork;
notifyListenersIfNecessary();
}
@@ -156,6 +159,7 @@
static class WifiState extends SignalController.State {
String ssid;
boolean isTransient;
+ boolean isDefault;
String statusLabel;
@Override
@@ -164,6 +168,7 @@
WifiState state = (WifiState) s;
ssid = state.ssid;
isTransient = state.isTransient;
+ isDefault = state.isDefault;
statusLabel = state.statusLabel;
}
@@ -172,6 +177,7 @@
super.toString(builder);
builder.append(",ssid=").append(ssid)
.append(",isTransient=").append(isTransient)
+ .append(",isDefault=").append(isDefault)
.append(",statusLabel=").append(statusLabel);
}
@@ -183,6 +189,7 @@
WifiState other = (WifiState) o;
return Objects.equals(other.ssid, ssid)
&& other.isTransient == isTransient
+ && other.isDefault == isDefault
&& TextUtils.equals(other.statusLabel, statusLabel);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java
index d4c6f4d..a29db4d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java
@@ -26,8 +26,6 @@
import android.annotation.IntDef;
import android.annotation.UiThread;
import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
import android.graphics.PixelFormat;
import android.provider.DeviceConfig;
import android.text.TextUtils;
@@ -47,9 +45,7 @@
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.Collections;
-import java.util.LinkedList;
import java.util.List;
-import java.util.Queue;
import java.util.Set;
/**
@@ -77,9 +73,6 @@
STATE_NOT_SHOWN,
STATE_APPEARING,
STATE_SHOWN,
- STATE_MINIMIZING,
- STATE_MINIMIZED,
- STATE_MAXIMIZING,
STATE_DISAPPEARING
})
public @interface State {}
@@ -88,13 +81,9 @@
private static final int STATE_NOT_SHOWN = 0;
private static final int STATE_APPEARING = 1;
private static final int STATE_SHOWN = 2;
- private static final int STATE_MINIMIZING = 3;
- private static final int STATE_MINIMIZED = 4;
- private static final int STATE_MAXIMIZING = 5;
- private static final int STATE_DISAPPEARING = 6;
+ private static final int STATE_DISAPPEARING = 3;
private static final int ANIMATION_DURATION = 600;
- private static final int MAXIMIZED_DURATION = 3000;
private final Context mContext;
private boolean mIsEnabled;
@@ -116,30 +105,6 @@
*/
private AudioActivityObserver[] mAudioActivityObservers;
/**
- * Whether the indicator should expand and show the recording application's label.
- * If disabled ({@code false}) the "minimized" ({@link #STATE_MINIMIZED}) indicator would appear
- * on the screen whenever an application is recording, but will not reveal to the user what
- * application this is.
- */
- private final boolean mRevealRecordingPackages;
- /**
- * Set of applications that we've notified the user about since the indicator came up. Meaning
- * that if an application is in this list then at some point since the indicator came up, it
- * was expanded showing this application's title.
- * Used not to notify the user about the same application again while the indicator is shown.
- * We empty this set every time the indicator goes off the screen (we always call {@code
- * mSessionNotifiedPackages.clear()} before calling {@link #hide()}).
- */
- private final Set<String> mSessionNotifiedPackages = new ArraySet<>();
- /**
- * If an application starts recording while the TV indicator is neither in {@link
- * #STATE_NOT_SHOWN} nor in {@link #STATE_MINIMIZED}, then we add the application's package
- * name to the queue, from which we take packages names one by one to disclose the
- * corresponding applications' titles to the user, whenever the indicator eventually comes to
- * one of the two aforementioned states.
- */
- private final Queue<String> mPendingNotificationPackages = new LinkedList<>();
- /**
* Set of applications for which we make an exception and do not show the indicator. This gets
* populated once - in {@link #AudioRecordingDisclosureBar(Context)}.
*/
@@ -149,8 +114,6 @@
mContext = context;
// Load configs
- mRevealRecordingPackages = mContext.getResources().getBoolean(
- R.bool.audio_recording_disclosure_reveal_packages);
reloadExemptPackages();
mIsEnabled = DeviceConfig.getBoolean(NAMESPACE_PRIVACY, ENABLED_FLAG, true);
@@ -210,10 +173,6 @@
if (mState != STATE_NOT_SHOWN) {
removeIndicatorView();
}
-
- // Clean up the state.
- mSessionNotifiedPackages.clear();
- mPendingNotificationPackages.clear();
}
@UiThread
@@ -231,68 +190,29 @@
}
if (active) {
- showIndicatorForPackageIfNeeded(packageName);
+ showIfNotShown();
} else {
hideIndicatorIfNeeded();
}
}
@UiThread
- private void showIndicatorForPackageIfNeeded(String packageName) {
- if (DEBUG) Log.d(TAG, "showIndicatorForPackageIfNeeded, packageName=" + packageName);
- if (!mSessionNotifiedPackages.add(packageName)) {
- // We've already notified user about this app, no need to do it again.
- if (DEBUG) Log.d(TAG, " - already notified");
- return;
- }
-
- switch (mState) {
- case STATE_NOT_SHOWN:
- show(packageName);
- break;
-
- case STATE_MINIMIZED:
- if (mRevealRecordingPackages) {
- expand(packageName);
- }
- break;
-
- case STATE_DISAPPEARING:
- case STATE_APPEARING:
- case STATE_MAXIMIZING:
- case STATE_SHOWN:
- case STATE_MINIMIZING:
- // Currently animating or expanded. Thus add to the pending notifications, and it
- // will be picked up once the indicator comes to the STATE_MINIMIZED.
- mPendingNotificationPackages.add(packageName);
- break;
- }
- }
-
- @UiThread
private void hideIndicatorIfNeeded() {
- // If not MINIMIZED, will check whether the indicator should be hidden when the indicator
- // comes to the STATE_MINIMIZED eventually.
- if (mState != STATE_MINIMIZED) return;
+ // If not STATE_APPEARING, will check whether the indicator should be hidden when the
+ // indicator comes to the STATE_SHOWN.
+ // If STATE_DISAPPEARING or STATE_SHOWN - nothing else for us to do here.
+ if (mState != STATE_SHOWN) return;
- // If is in the STATE_MINIMIZED, but there are other active recorders - simply ignore.
- for (int index = mAudioActivityObservers.length - 1; index >= 0; index--) {
- for (String activePackage : mAudioActivityObservers[index].getActivePackages()) {
- if (mExemptPackages.contains(activePackage)) continue;
- return;
- }
+ // If is in the STATE_SHOWN and there are no active recorders - hide.
+ if (!hasActiveRecorders()) {
+ hide();
}
-
- // Clear the state and hide the indicator.
- mSessionNotifiedPackages.clear();
- hide();
}
@UiThread
- private void show(String packageName) {
- if (DEBUG) {
- Log.d(TAG, "Showing indicator");
- }
+ private void showIfNotShown() {
+ if (mState != STATE_NOT_SHOWN) return;
+ if (DEBUG) Log.d(TAG, "Showing indicator");
mIsLtr = mContext.getResources().getConfiguration().getLayoutDirection()
== View.LAYOUT_DIRECTION_LTR;
@@ -308,30 +228,14 @@
mTextView = mTextsContainers.findViewById(R.id.text);
mBgEnd = mIndicatorView.findViewById(R.id.bg_end);
- // Set up the notification text
- if (mRevealRecordingPackages) {
- // Swap background drawables depending on layout directions (both drawables have rounded
- // corners only on one side)
- if (mIsLtr) {
- mBgEnd.setBackgroundResource(R.drawable.tv_rect_dark_right_rounded);
- mIconContainerBg.setBackgroundResource(R.drawable.tv_rect_dark_left_rounded);
- } else {
- mBgEnd.setBackgroundResource(R.drawable.tv_rect_dark_left_rounded);
- mIconContainerBg.setBackgroundResource(R.drawable.tv_rect_dark_right_rounded);
- }
-
- final String label = getApplicationLabel(packageName);
- mTextView.setText(mContext.getString(R.string.app_accessed_mic, label));
- } else {
- mTextsContainers.setVisibility(View.GONE);
- mIconContainerBg.setVisibility(View.GONE);
- mTextView.setVisibility(View.GONE);
- mBgEnd.setVisibility(View.GONE);
- mTextsContainers = null;
- mIconContainerBg = null;
- mTextView = null;
- mBgEnd = null;
- }
+ mTextsContainers.setVisibility(View.GONE);
+ mIconContainerBg.setVisibility(View.GONE);
+ mTextView.setVisibility(View.GONE);
+ mBgEnd.setVisibility(View.GONE);
+ mTextsContainers = null;
+ mIconContainerBg = null;
+ mTextView = null;
+ mBgEnd = null;
// Initially change the visibility to INVISIBLE, wait until and receives the size and
// then animate it moving from "off" the screen correctly
@@ -366,9 +270,7 @@
@Override
public void onAnimationStart(Animator animation,
boolean isReverse) {
- if (mState == STATE_STOPPED) {
- return;
- }
+ if (mState == STATE_STOPPED) return;
// Indicator is INVISIBLE at the moment, change it.
mIndicatorView.setVisibility(View.VISIBLE);
@@ -376,11 +278,7 @@
@Override
public void onAnimationEnd(Animator animation) {
- if (mRevealRecordingPackages) {
- onExpanded();
- } else {
- onMinimized();
- }
+ onAppeared();
}
});
set.start();
@@ -404,60 +302,9 @@
}
@UiThread
- private void expand(String packageName) {
- assertRevealingRecordingPackages();
-
- final String label = getApplicationLabel(packageName);
- mTextView.setText(mContext.getString(R.string.app_accessed_mic, label));
-
- final AnimatorSet set = new AnimatorSet();
- set.playTogether(
- ObjectAnimator.ofFloat(mIconTextsContainer, View.TRANSLATION_X, 0),
- ObjectAnimator.ofFloat(mIconContainerBg, View.ALPHA, 1f),
- ObjectAnimator.ofFloat(mTextsContainers, View.ALPHA, 1f),
- ObjectAnimator.ofFloat(mBgEnd, View.ALPHA, 1f));
- set.setDuration(ANIMATION_DURATION);
- set.addListener(
- new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- onExpanded();
- }
- });
- set.start();
-
- mState = STATE_MAXIMIZING;
- }
-
- @UiThread
- private void minimize() {
- assertRevealingRecordingPackages();
-
- final int targetOffset = (mIsLtr ? 1 : -1) * mTextsContainers.getWidth();
- final AnimatorSet set = new AnimatorSet();
- set.playTogether(
- ObjectAnimator.ofFloat(mIconTextsContainer, View.TRANSLATION_X, targetOffset),
- ObjectAnimator.ofFloat(mIconContainerBg, View.ALPHA, 0f),
- ObjectAnimator.ofFloat(mTextsContainers, View.ALPHA, 0f),
- ObjectAnimator.ofFloat(mBgEnd, View.ALPHA, 0f));
- set.setDuration(ANIMATION_DURATION);
- set.addListener(
- new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- onMinimized();
- }
- });
- set.start();
-
- mState = STATE_MINIMIZING;
- }
-
- @UiThread
private void hide() {
- if (DEBUG) {
- Log.d(TAG, "Hide indicator");
- }
+ if (DEBUG) Log.d(TAG, "Hide indicator");
+
final int targetOffset = (mIsLtr ? 1 : -1) * (mIndicatorView.getWidth()
- (int) mIconTextsContainer.getTranslationX());
final AnimatorSet set = new AnimatorSet();
@@ -477,51 +324,39 @@
mState = STATE_DISAPPEARING;
}
- @UiThread
- private void onExpanded() {
- if (mState == STATE_STOPPED) {
- return;
- }
- assertRevealingRecordingPackages();
+ @UiThread
+ private void onAppeared() {
+ if (mState == STATE_STOPPED) return;
mState = STATE_SHOWN;
- mIndicatorView.postDelayed(this::minimize, MAXIMIZED_DURATION);
- }
-
- @UiThread
- private void onMinimized() {
- if (mState == STATE_STOPPED) {
- return;
- }
-
- mState = STATE_MINIMIZED;
-
- if (mRevealRecordingPackages && !mPendingNotificationPackages.isEmpty()) {
- // There is a new application that started recording, tell the user about it.
- expand(mPendingNotificationPackages.poll());
- } else {
- hideIndicatorIfNeeded();
- }
+ hideIndicatorIfNeeded();
}
@UiThread
private void onHidden() {
- if (mState == STATE_STOPPED) {
- return;
- }
+ if (mState == STATE_STOPPED) return;
removeIndicatorView();
mState = STATE_NOT_SHOWN;
- // Check if anybody started recording while we were in STATE_DISAPPEARING
- if (!mPendingNotificationPackages.isEmpty()) {
- // There is a new application that started recording, tell the user about it.
- show(mPendingNotificationPackages.poll());
+ if (hasActiveRecorders()) {
+ // Got new recorders, show again.
+ showIfNotShown();
}
}
+ private boolean hasActiveRecorders() {
+ for (int index = mAudioActivityObservers.length - 1; index >= 0; index--) {
+ for (String activePackage : mAudioActivityObservers[index].getActivePackages()) {
+ if (mExemptPackages.contains(activePackage)) continue;
+ return true;
+ }
+ }
+ return false;
+ }
+
private void removeIndicatorView() {
final WindowManager windowManager = (WindowManager) mContext.getSystemService(
Context.WINDOW_SERVICE);
@@ -536,26 +371,6 @@
mBgEnd = null;
}
- private String getApplicationLabel(String packageName) {
- assertRevealingRecordingPackages();
-
- final PackageManager pm = mContext.getPackageManager();
- final ApplicationInfo appInfo;
- try {
- appInfo = pm.getApplicationInfo(packageName, 0);
- } catch (PackageManager.NameNotFoundException e) {
- return packageName;
- }
- return pm.getApplicationLabel(appInfo).toString();
- }
-
- private void assertRevealingRecordingPackages() {
- if (!mRevealRecordingPackages) {
- Log.e(TAG, "Not revealing recording packages",
- DEBUG ? new RuntimeException("Should not be called") : null);
- }
- }
-
private static List<String> splitByComma(String string) {
return TextUtils.isEmpty(string) ? Collections.emptyList() : Arrays.asList(
string.split(","));
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
index 0baecd5..228b2ea 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
@@ -45,12 +45,14 @@
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.phone.DozeServiceHost;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.NotificationShadeWindowControllerImpl;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.ShadeControllerImpl;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -162,5 +164,9 @@
StatusBarKeyguardViewManager statusBarKeyguardViewManager);
@Binds
+ abstract NotificationShadeWindowController bindNotificationShadeController(
+ NotificationShadeWindowControllerImpl notificationShadeWindowController);
+
+ @Binds
abstract DozeHost provideDozeHost(DozeServiceHost dozeServiceHost);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
index 60f0cd9..e967a5d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
@@ -82,8 +82,7 @@
allowTestableLooperAsMainThread();
MockitoAnnotations.initMocks(this);
- mFsc = new ForegroundServiceController(
- mEntryManager, mAppOpsController, mMainHandler);
+ mFsc = new ForegroundServiceController(mAppOpsController, mMainHandler);
mListener = new ForegroundServiceNotificationListener(
mContext, mFsc, mEntryManager, mNotifPipeline,
mock(ForegroundServiceLifetimeExtender.class), mClock);
@@ -115,85 +114,6 @@
}
@Test
- public void testAppOps_appOpChangedBeforeNotificationExists() {
- // GIVEN app op exists, but notification doesn't exist in NEM yet
- NotificationEntry entry = createFgEntry();
- mFsc.onAppOpChanged(
- AppOpsManager.OP_CAMERA,
- entry.getSbn().getUid(),
- entry.getSbn().getPackageName(),
- true);
- assertFalse(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA));
-
- // WHEN the notification is added
- mEntryListener.onPendingEntryAdded(entry);
-
- // THEN the app op is added to the entry
- Assert.assertTrue(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA));
- }
-
- @Test
- public void testAppOps_appOpAddedToForegroundNotif() {
- // GIVEN a notification associated with a foreground service
- NotificationEntry entry = addFgEntry();
- when(mEntryManager.getPendingOrActiveNotif(entry.getKey())).thenReturn(entry);
-
- // WHEN we are notified of a new app op for this notification
- mFsc.onAppOpChanged(
- AppOpsManager.OP_CAMERA,
- entry.getSbn().getUid(),
- entry.getSbn().getPackageName(),
- true);
-
- // THEN the app op is added to the entry
- Assert.assertTrue(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA));
-
- // THEN notification views are updated since the notification is visible
- verify(mEntryManager, times(1)).updateNotifications(anyString());
- }
-
- @Test
- public void testAppOpsAlreadyAdded() {
- // GIVEN a foreground service associated notification that already has the correct app op
- NotificationEntry entry = addFgEntry();
- entry.mActiveAppOps.add(AppOpsManager.OP_CAMERA);
- when(mEntryManager.getPendingOrActiveNotif(entry.getKey())).thenReturn(entry);
-
- // WHEN we are notified of the same app op for this notification
- mFsc.onAppOpChanged(
- AppOpsManager.OP_CAMERA,
- entry.getSbn().getUid(),
- entry.getSbn().getPackageName(),
- true);
-
- // THEN the app op still exists in the notification entry
- Assert.assertTrue(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA));
-
- // THEN notification views aren't updated since nothing changed
- verify(mEntryManager, never()).updateNotifications(anyString());
- }
-
- @Test
- public void testAppOps_appOpNotAddedToUnrelatedNotif() {
- // GIVEN no notification entries correspond to the newly updated appOp
- NotificationEntry entry = addFgEntry();
- when(mEntryManager.getPendingOrActiveNotif(entry.getKey())).thenReturn(null);
-
- // WHEN a new app op is detected
- mFsc.onAppOpChanged(
- AppOpsManager.OP_CAMERA,
- entry.getSbn().getUid(),
- entry.getSbn().getPackageName(),
- true);
-
- // THEN we won't see appOps on the entry
- Assert.assertFalse(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA));
-
- // THEN notification views aren't updated since nothing changed
- verify(mEntryManager, never()).updateNotifications(anyString());
- }
-
- @Test
public void testAppOpsCRUD() {
// no crash on remove that doesn't exist
mFsc.onAppOpChanged(9, 1000, "pkg1", false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 5c86fcb..a7808ad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -60,7 +60,6 @@
import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.SystemUIFactory;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dump.DumpManager;
@@ -71,9 +70,7 @@
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationRemoveInterceptor;
-import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.RankingBuilder;
-import com.android.systemui.statusbar.SuperStatusBarViewFactory;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -87,7 +84,7 @@
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.LockscreenLockIconController;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.NotificationShadeWindowControllerImpl;
import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -95,7 +92,6 @@
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.util.FloatingContentCoordinator;
-import com.android.systemui.util.InjectionInflationController;
import com.google.common.collect.ImmutableList;
@@ -159,7 +155,7 @@
private ArgumentCaptor<NotificationRemoveInterceptor> mRemoveInterceptorCaptor;
private TestableBubbleController mBubbleController;
- private NotificationShadeWindowController mNotificationShadeWindowController;
+ private NotificationShadeWindowControllerImpl mNotificationShadeWindowController;
private NotificationEntryListener mEntryListener;
private NotificationRemoveInterceptor mRemoveInterceptor;
@@ -199,8 +195,6 @@
private TestableLooper mTestableLooper;
- private SuperStatusBarViewFactory mSuperStatusBarViewFactory;
-
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -210,25 +204,8 @@
mContext.addMockSystemService(FaceManager.class, mFaceManager);
when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors);
- mSuperStatusBarViewFactory = new SuperStatusBarViewFactory(mContext,
- new InjectionInflationController(SystemUIFactory.getInstance().getRootComponent()
- .createViewInstanceCreatorFactory()),
- new NotificationShelfComponent.Builder() {
- @Override
- public NotificationShelfComponent.Builder notificationShelf(
- NotificationShelf view) {
- return this;
- }
-
- @Override
- public NotificationShelfComponent build() {
- return mNotificationShelfComponent;
- }
- },
- mLockIconController);
-
// Bubbles get added to status bar window view
- mNotificationShadeWindowController = new NotificationShadeWindowController(mContext,
+ mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext,
mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController,
mColorExtractor, mDumpManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
index 196aa65..3df0df6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
@@ -83,7 +83,7 @@
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.LockscreenLockIconController;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.NotificationShadeWindowControllerImpl;
import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -153,7 +153,7 @@
@Captor
private ArgumentCaptor<NotifCollectionListener> mNotifListenerCaptor;
private TestableBubbleController mBubbleController;
- private NotificationShadeWindowController mNotificationShadeWindowController;
+ private NotificationShadeWindowControllerImpl mNotificationShadeWindowController;
private NotifCollectionListener mEntryListener;
private NotificationTestHelper mNotificationTestHelper;
private ExpandableNotificationRow mRow;
@@ -218,7 +218,7 @@
mLockIconController);
// Bubbles get added to status bar window view
- mNotificationShadeWindowController = new NotificationShadeWindowController(mContext,
+ mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext,
mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController,
mColorExtractor, mDumpManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
index 0a6d071..51ca2a4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
@@ -27,11 +27,11 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ZenModeController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
index 4c54954..c8e0f49 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
@@ -71,7 +71,7 @@
import com.android.systemui.plugins.GlobalActionsPanelPlugin;
import com.android.systemui.settings.CurrentUserContextTracker;
import com.android.systemui.statusbar.NotificationShadeDepthController;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.RingerModeLiveData;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
index 2e794a4..89538ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
@@ -20,7 +20,6 @@
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
@@ -34,19 +33,23 @@
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
import java.util.ArrayList;
-import java.util.Map;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class MediaDataCombineLatestTest extends SysuiTestCase {
+ @Rule public MockitoRule mockito = MockitoJUnit.rule();
+
private static final String KEY = "TEST_KEY";
private static final String OLD_KEY = "TEST_KEY_OLD";
private static final String APP = "APP";
@@ -59,27 +62,14 @@
private MediaDataCombineLatest mManager;
- @Mock private MediaDataManager mDataSource;
- @Mock private MediaDeviceManager mDeviceSource;
@Mock private MediaDataManager.Listener mListener;
- private MediaDataManager.Listener mDataListener;
- private MediaDeviceManager.Listener mDeviceListener;
-
private MediaData mMediaData;
private MediaDeviceData mDeviceData;
@Before
public void setUp() {
- mDataSource = mock(MediaDataManager.class);
- mDeviceSource = mock(MediaDeviceManager.class);
- mListener = mock(MediaDataManager.Listener.class);
-
- mManager = new MediaDataCombineLatest(mDataSource, mDeviceSource);
-
- mDataListener = captureDataListener();
- mDeviceListener = captureDeviceListener();
-
+ mManager = new MediaDataCombineLatest();
mManager.addListener(mListener);
mMediaData = new MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null,
@@ -91,7 +81,7 @@
@Test
public void eventNotEmittedWithoutDevice() {
// WHEN data source emits an event without device data
- mDataListener.onMediaDataLoaded(KEY, null, mMediaData);
+ mManager.onMediaDataLoaded(KEY, null, mMediaData);
// THEN an event isn't emitted
verify(mListener, never()).onMediaDataLoaded(eq(KEY), any(), any());
}
@@ -99,7 +89,7 @@
@Test
public void eventNotEmittedWithoutMedia() {
// WHEN device source emits an event without media data
- mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData);
+ mManager.onMediaDeviceChanged(KEY, null, mDeviceData);
// THEN an event isn't emitted
verify(mListener, never()).onMediaDataLoaded(eq(KEY), any(), any());
}
@@ -107,9 +97,9 @@
@Test
public void emitEventAfterDeviceFirst() {
// GIVEN that a device event has already been received
- mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData);
+ mManager.onMediaDeviceChanged(KEY, null, mDeviceData);
// WHEN media event is received
- mDataListener.onMediaDataLoaded(KEY, null, mMediaData);
+ mManager.onMediaDataLoaded(KEY, null, mMediaData);
// THEN the listener receives a combined event
ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
verify(mListener).onMediaDataLoaded(eq(KEY), any(), captor.capture());
@@ -119,9 +109,9 @@
@Test
public void emitEventAfterMediaFirst() {
// GIVEN that media event has already been received
- mDataListener.onMediaDataLoaded(KEY, null, mMediaData);
+ mManager.onMediaDataLoaded(KEY, null, mMediaData);
// WHEN device event is received
- mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData);
+ mManager.onMediaDeviceChanged(KEY, null, mDeviceData);
// THEN the listener receives a combined event
ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
verify(mListener).onMediaDataLoaded(eq(KEY), any(), captor.capture());
@@ -131,11 +121,11 @@
@Test
public void migrateKeyMediaFirst() {
// GIVEN that media and device info has already been received
- mDataListener.onMediaDataLoaded(OLD_KEY, null, mMediaData);
- mDeviceListener.onMediaDeviceChanged(OLD_KEY, null, mDeviceData);
+ mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData);
+ mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData);
reset(mListener);
// WHEN a key migration event is received
- mDataListener.onMediaDataLoaded(KEY, OLD_KEY, mMediaData);
+ mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData);
// THEN the listener receives a combined event
ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
verify(mListener).onMediaDataLoaded(eq(KEY), eq(OLD_KEY), captor.capture());
@@ -145,11 +135,11 @@
@Test
public void migrateKeyDeviceFirst() {
// GIVEN that media and device info has already been received
- mDataListener.onMediaDataLoaded(OLD_KEY, null, mMediaData);
- mDeviceListener.onMediaDeviceChanged(OLD_KEY, null, mDeviceData);
+ mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData);
+ mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData);
reset(mListener);
// WHEN a key migration event is received
- mDeviceListener.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData);
+ mManager.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData);
// THEN the listener receives a combined event
ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
verify(mListener).onMediaDataLoaded(eq(KEY), eq(OLD_KEY), captor.capture());
@@ -159,12 +149,12 @@
@Test
public void migrateKeyMediaAfter() {
// GIVEN that media and device info has already been received
- mDataListener.onMediaDataLoaded(OLD_KEY, null, mMediaData);
- mDeviceListener.onMediaDeviceChanged(OLD_KEY, null, mDeviceData);
- mDeviceListener.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData);
+ mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData);
+ mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData);
+ mManager.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData);
reset(mListener);
// WHEN a second key migration event is received for media
- mDataListener.onMediaDataLoaded(KEY, OLD_KEY, mMediaData);
+ mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData);
// THEN the key has already been migrated
ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
verify(mListener).onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture());
@@ -174,12 +164,12 @@
@Test
public void migrateKeyDeviceAfter() {
// GIVEN that media and device info has already been received
- mDataListener.onMediaDataLoaded(OLD_KEY, null, mMediaData);
- mDeviceListener.onMediaDeviceChanged(OLD_KEY, null, mDeviceData);
- mDataListener.onMediaDataLoaded(KEY, OLD_KEY, mMediaData);
+ mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData);
+ mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData);
+ mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData);
reset(mListener);
// WHEN a second key migration event is received for the device
- mDeviceListener.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData);
+ mManager.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData);
// THEN the key has already be migrated
ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
verify(mListener).onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture());
@@ -189,60 +179,34 @@
@Test
public void mediaDataRemoved() {
// WHEN media data is removed without first receiving device or data
- mDataListener.onMediaDataRemoved(KEY);
+ mManager.onMediaDataRemoved(KEY);
// THEN a removed event isn't emitted
verify(mListener, never()).onMediaDataRemoved(eq(KEY));
}
@Test
public void mediaDataRemovedAfterMediaEvent() {
- mDataListener.onMediaDataLoaded(KEY, null, mMediaData);
- mDataListener.onMediaDataRemoved(KEY);
+ mManager.onMediaDataLoaded(KEY, null, mMediaData);
+ mManager.onMediaDataRemoved(KEY);
verify(mListener).onMediaDataRemoved(eq(KEY));
}
@Test
public void mediaDataRemovedAfterDeviceEvent() {
- mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData);
- mDataListener.onMediaDataRemoved(KEY);
+ mManager.onMediaDeviceChanged(KEY, null, mDeviceData);
+ mManager.onMediaDataRemoved(KEY);
verify(mListener).onMediaDataRemoved(eq(KEY));
}
@Test
public void mediaDataKeyUpdated() {
// GIVEN that device and media events have already been received
- mDataListener.onMediaDataLoaded(KEY, null, mMediaData);
- mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData);
+ mManager.onMediaDataLoaded(KEY, null, mMediaData);
+ mManager.onMediaDeviceChanged(KEY, null, mDeviceData);
// WHEN the key is changed
- mDataListener.onMediaDataLoaded("NEW_KEY", KEY, mMediaData);
+ mManager.onMediaDataLoaded("NEW_KEY", KEY, mMediaData);
// THEN the listener gets a load event with the correct keys
ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
verify(mListener).onMediaDataLoaded(eq("NEW_KEY"), any(), captor.capture());
}
-
- @Test
- public void getDataIncludesDevice() {
- // GIVEN that device and media events have been received
- mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData);
- mDataListener.onMediaDataLoaded(KEY, null, mMediaData);
-
- // THEN the result of getData includes device info
- Map<String, MediaData> results = mManager.getData();
- assertThat(results.get(KEY)).isNotNull();
- assertThat(results.get(KEY).getDevice()).isEqualTo(mDeviceData);
- }
-
- private MediaDataManager.Listener captureDataListener() {
- ArgumentCaptor<MediaDataManager.Listener> captor = ArgumentCaptor.forClass(
- MediaDataManager.Listener.class);
- verify(mDataSource).addListener(captor.capture());
- return captor.getValue();
- }
-
- private MediaDeviceManager.Listener captureDeviceListener() {
- ArgumentCaptor<MediaDeviceManager.Listener> captor = ArgumentCaptor.forClass(
- MediaDeviceManager.Listener.class);
- verify(mDeviceSource).addListener(captor.capture());
- return captor.getValue();
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
index afb64a7..36b6527 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
@@ -32,6 +32,7 @@
import org.mockito.Mockito
import org.mockito.Mockito.`when`
import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
import java.util.concurrent.Executor
@@ -56,8 +57,6 @@
class MediaDataFilterTest : SysuiTestCase() {
@Mock
- private lateinit var combineLatest: MediaDataCombineLatest
- @Mock
private lateinit var listener: MediaDataManager.Listener
@Mock
private lateinit var broadcastDispatcher: BroadcastDispatcher
@@ -78,8 +77,9 @@
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- mediaDataFilter = MediaDataFilter(combineLatest, broadcastDispatcher, mediaResumeListener,
- mediaDataManager, lockscreenUserManager, executor)
+ mediaDataFilter = MediaDataFilter(broadcastDispatcher, mediaResumeListener,
+ lockscreenUserManager, executor)
+ mediaDataFilter.mediaDataManager = mediaDataManager
mediaDataFilter.addListener(listener)
// Start all tests as main user
@@ -152,8 +152,9 @@
@Test
fun testOnUserSwitched_addsNewUserControls() {
// GIVEN that we had some media for both users
- val dataMap = mapOf(KEY to dataMain, KEY_ALT to dataGuest)
- `when`(combineLatest.getData()).thenReturn(dataMap)
+ mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
+ mediaDataFilter.onMediaDataLoaded(KEY_ALT, null, dataGuest)
+ reset(listener)
// and we switch to guest user
setUser(USER_GUEST)
@@ -213,4 +214,4 @@
verify(mediaDataManager).setTimedOut(eq(KEY), eq(true))
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
index 457d559..84c1bf9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -16,6 +16,7 @@
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.SbnBuilder
import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
@@ -24,9 +25,13 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
import org.mockito.Mockito.`when` as whenever
@@ -48,6 +53,7 @@
@RunWith(AndroidTestingRunner::class)
class MediaDataManagerTest : SysuiTestCase() {
+ @JvmField @Rule val mockito = MockitoJUnit.rule()
@Mock lateinit var mediaControllerFactory: MediaControllerFactory
@Mock lateinit var controller: MediaController
lateinit var session: MediaSession
@@ -58,20 +64,38 @@
@Mock lateinit var broadcastDispatcher: BroadcastDispatcher
@Mock lateinit var mediaTimeoutListener: MediaTimeoutListener
@Mock lateinit var mediaResumeListener: MediaResumeListener
+ @Mock lateinit var mediaSessionBasedFilter: MediaSessionBasedFilter
+ @Mock lateinit var mediaDeviceManager: MediaDeviceManager
+ @Mock lateinit var mediaDataCombineLatest: MediaDataCombineLatest
+ @Mock lateinit var mediaDataFilter: MediaDataFilter
+ @Mock lateinit var listener: MediaDataManager.Listener
@Mock lateinit var pendingIntent: PendingIntent
@Mock lateinit var activityStarter: ActivityStarter
- @JvmField @Rule val mockito = MockitoJUnit.rule()
lateinit var mediaDataManager: MediaDataManager
lateinit var mediaNotification: StatusBarNotification
+ @Captor lateinit var mediaDataCaptor: ArgumentCaptor<MediaData>
@Before
fun setup() {
foregroundExecutor = FakeExecutor(FakeSystemClock())
backgroundExecutor = FakeExecutor(FakeSystemClock())
- mediaDataManager = MediaDataManager(context, backgroundExecutor, foregroundExecutor,
- mediaControllerFactory, broadcastDispatcher, dumpManager,
- mediaTimeoutListener, mediaResumeListener, activityStarter,
- useMediaResumption = true, useQsMediaPlayer = true)
+ mediaDataManager = MediaDataManager(
+ context = context,
+ backgroundExecutor = backgroundExecutor,
+ foregroundExecutor = foregroundExecutor,
+ mediaControllerFactory = mediaControllerFactory,
+ broadcastDispatcher = broadcastDispatcher,
+ dumpManager = dumpManager,
+ mediaTimeoutListener = mediaTimeoutListener,
+ mediaResumeListener = mediaResumeListener,
+ mediaSessionBasedFilter = mediaSessionBasedFilter,
+ mediaDeviceManager = mediaDeviceManager,
+ mediaDataCombineLatest = mediaDataCombineLatest,
+ mediaDataFilter = mediaDataFilter,
+ activityStarter = activityStarter,
+ useMediaResumption = true,
+ useQsMediaPlayer = true
+ )
session = MediaSession(context, "MediaDataManagerTestSession")
mediaNotification = SbnBuilder().run {
setPkg(PACKAGE_NAME)
@@ -86,6 +110,12 @@
putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_TITLE)
}
whenever(mediaControllerFactory.create(eq(session.sessionToken))).thenReturn(controller)
+
+ // This is an ugly hack for now. The mediaSessionBasedFilter is one of the internal
+ // listeners in the internal processing pipeline. It receives events, but ince it is a
+ // mock, it doesn't pass those events along the chain to the external listeners. So, just
+ // treat mediaSessionBasedFilter as a listener for testing.
+ listener = mediaSessionBasedFilter
}
@After
@@ -115,8 +145,6 @@
@Test
fun testOnMetaDataLoaded_callsListener() {
- val listener = mock(MediaDataManager.Listener::class.java)
- mediaDataManager.addListener(listener)
mediaDataManager.onNotificationAdded(KEY, mediaNotification)
mediaDataManager.onMediaDataLoaded(KEY, oldKey = null, data = mock(MediaData::class.java))
verify(listener).onMediaDataLoaded(eq(KEY), eq(null), anyObject())
@@ -124,90 +152,81 @@
@Test
fun testOnMetaDataLoaded_conservesActiveFlag() {
- val listener = TestListener()
whenever(mediaControllerFactory.create(anyObject())).thenReturn(controller)
whenever(controller.metadata).thenReturn(metadataBuilder.build())
mediaDataManager.addListener(listener)
mediaDataManager.onNotificationAdded(KEY, mediaNotification)
assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
- assertThat(listener.data!!.active).isTrue()
+ verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor))
+ assertThat(mediaDataCaptor.value!!.active).isTrue()
}
@Test
fun testOnNotificationRemoved_callsListener() {
- val listener = mock(MediaDataManager.Listener::class.java)
- mediaDataManager.addListener(listener)
mediaDataManager.onNotificationAdded(KEY, mediaNotification)
mediaDataManager.onMediaDataLoaded(KEY, oldKey = null, data = mock(MediaData::class.java))
mediaDataManager.onNotificationRemoved(KEY)
-
verify(listener).onMediaDataRemoved(eq(KEY))
}
@Test
fun testOnNotificationRemoved_withResumption() {
// GIVEN that the manager has a notification with a resume action
- val listener = TestListener()
- mediaDataManager.addListener(listener)
whenever(controller.metadata).thenReturn(metadataBuilder.build())
mediaDataManager.onNotificationAdded(KEY, mediaNotification)
assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
- val data = listener.data!!
+ verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor))
+ val data = mediaDataCaptor.value
assertThat(data.resumption).isFalse()
mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {}))
// WHEN the notification is removed
mediaDataManager.onNotificationRemoved(KEY)
// THEN the media data indicates that it is for resumption
- assertThat(listener.data!!.resumption).isTrue()
- // AND the new key is the package name
- assertThat(listener.key!!).isEqualTo(PACKAGE_NAME)
- assertThat(listener.oldKey!!).isEqualTo(KEY)
- assertThat(listener.removedKey).isNull()
+ verify(listener).onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor))
+ assertThat(mediaDataCaptor.value.resumption).isTrue()
}
@Test
fun testOnNotificationRemoved_twoWithResumption() {
// GIVEN that the manager has two notifications with resume actions
- val listener = TestListener()
- mediaDataManager.addListener(listener)
whenever(controller.metadata).thenReturn(metadataBuilder.build())
mediaDataManager.onNotificationAdded(KEY, mediaNotification)
mediaDataManager.onNotificationAdded(KEY_2, mediaNotification)
assertThat(backgroundExecutor.runAllReady()).isEqualTo(2)
assertThat(foregroundExecutor.runAllReady()).isEqualTo(2)
- val data = listener.data!!
+ verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor))
+ val data = mediaDataCaptor.value
assertThat(data.resumption).isFalse()
val resumableData = data.copy(resumeAction = Runnable {})
mediaDataManager.onMediaDataLoaded(KEY, null, resumableData)
mediaDataManager.onMediaDataLoaded(KEY_2, null, resumableData)
+ reset(listener)
// WHEN the first is removed
mediaDataManager.onNotificationRemoved(KEY)
// THEN the data is for resumption and the key is migrated to the package name
- assertThat(listener.data!!.resumption).isTrue()
- assertThat(listener.key!!).isEqualTo(PACKAGE_NAME)
- assertThat(listener.oldKey!!).isEqualTo(KEY)
- assertThat(listener.removedKey).isNull()
+ verify(listener).onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor))
+ assertThat(mediaDataCaptor.value.resumption).isTrue()
+ verify(listener, never()).onMediaDataRemoved(eq(KEY))
// WHEN the second is removed
mediaDataManager.onNotificationRemoved(KEY_2)
// THEN the data is for resumption and the second key is removed
- assertThat(listener.data!!.resumption).isTrue()
- assertThat(listener.key!!).isEqualTo(PACKAGE_NAME)
- assertThat(listener.oldKey!!).isEqualTo(PACKAGE_NAME)
- assertThat(listener.removedKey!!).isEqualTo(KEY_2)
+ verify(listener).onMediaDataLoaded(eq(PACKAGE_NAME), eq(PACKAGE_NAME),
+ capture(mediaDataCaptor))
+ assertThat(mediaDataCaptor.value.resumption).isTrue()
+ verify(listener).onMediaDataRemoved(eq(KEY_2))
}
@Test
fun testAppBlockedFromResumption() {
// GIVEN that the manager has a notification with a resume action
- val listener = TestListener()
- mediaDataManager.addListener(listener)
whenever(controller.metadata).thenReturn(metadataBuilder.build())
mediaDataManager.onNotificationAdded(KEY, mediaNotification)
assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
- val data = listener.data!!
+ verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor))
+ val data = mediaDataCaptor.value
assertThat(data.resumption).isFalse()
mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {}))
@@ -219,7 +238,7 @@
mediaDataManager.onNotificationRemoved(KEY)
// THEN the media data is removed
- assertThat(listener.removedKey!!).isEqualTo(KEY)
+ verify(listener).onMediaDataRemoved(eq(KEY))
}
@Test
@@ -229,13 +248,12 @@
mediaDataManager.appsBlockedFromResume = blocked
// and GIVEN that the manager has a notification from that app with a resume action
- val listener = TestListener()
- mediaDataManager.addListener(listener)
whenever(controller.metadata).thenReturn(metadataBuilder.build())
mediaDataManager.onNotificationAdded(KEY, mediaNotification)
assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
- val data = listener.data!!
+ verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor))
+ val data = mediaDataCaptor.value
assertThat(data.resumption).isFalse()
mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {}))
@@ -246,14 +264,11 @@
mediaDataManager.onNotificationRemoved(KEY)
// THEN the entry will stay as a resume control
- assertThat(listener.key!!).isEqualTo(PACKAGE_NAME)
- assertThat(listener.oldKey!!).isEqualTo(KEY)
+ verify(listener).onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor))
}
@Test
fun testAddResumptionControls() {
- val listener = TestListener()
- mediaDataManager.addListener(listener)
// WHEN resumption controls are added`
val desc = MediaDescription.Builder().run {
setTitle(SESSION_TITLE)
@@ -264,7 +279,8 @@
assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
// THEN the media data indicates that it is for resumption
- val data = listener.data!!
+ verify(listener).onMediaDataLoaded(eq(PACKAGE_NAME), eq(null), capture(mediaDataCaptor))
+ val data = mediaDataCaptor.value
assertThat(data.resumption).isTrue()
assertThat(data.song).isEqualTo(SESSION_TITLE)
assertThat(data.app).isEqualTo(APP_NAME)
@@ -273,8 +289,6 @@
@Test
fun testDismissMedia_listenerCalled() {
- val listener = mock(MediaDataManager.Listener::class.java)
- mediaDataManager.addListener(listener)
mediaDataManager.onNotificationAdded(KEY, mediaNotification)
mediaDataManager.onMediaDataLoaded(KEY, oldKey = null, data = mock(MediaData::class.java))
mediaDataManager.dismissMediaData(KEY, 0L)
@@ -284,26 +298,4 @@
verify(listener).onMediaDataRemoved(eq(KEY))
}
-
- /**
- * Simple implementation of [MediaDataManager.Listener] for the test.
- *
- * Giving up on trying to get a mock Listener and ArgumentCaptor to work.
- */
- private class TestListener : MediaDataManager.Listener {
- var data: MediaData? = null
- var key: String? = null
- var oldKey: String? = null
- var removedKey: String? = null
-
- override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) {
- this.key = key
- this.oldKey = oldKey
- this.data = data
- }
-
- override fun onMediaDataRemoved(key: String) {
- removedKey = key
- }
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
index 7bc15dd..fdb432c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
@@ -68,7 +68,6 @@
public class MediaDeviceManagerTest : SysuiTestCase() {
private lateinit var manager: MediaDeviceManager
- @Mock private lateinit var mediaDataManager: MediaDataManager
@Mock private lateinit var lmmFactory: LocalMediaManagerFactory
@Mock private lateinit var lmm: LocalMediaManager
@Mock private lateinit var mr2: MediaRouter2Manager
@@ -91,7 +90,7 @@
fakeFgExecutor = FakeExecutor(FakeSystemClock())
fakeBgExecutor = FakeExecutor(FakeSystemClock())
manager = MediaDeviceManager(context, lmmFactory, mr2, fakeFgExecutor, fakeBgExecutor,
- mediaDataManager, dumpster)
+ dumpster)
manager.addListener(listener)
// Configure mocks.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt
new file mode 100644
index 0000000..887cc77
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2020 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.systemui.media
+
+import android.graphics.Color
+import android.media.session.MediaController
+import android.media.session.MediaController.PlaybackInfo
+import android.media.session.MediaSession
+import android.media.session.MediaSessionManager
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.time.FakeSystemClock
+
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.any
+import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+import org.mockito.Mockito.`when` as whenever
+
+private const val PACKAGE = "PKG"
+private const val KEY = "TEST_KEY"
+private const val NOTIF_KEY = "TEST_KEY"
+private const val SESSION_ARTIST = "SESSION_ARTIST"
+private const val SESSION_TITLE = "SESSION_TITLE"
+private const val APP_NAME = "APP_NAME"
+private const val USER_ID = 0
+
+private val info = MediaData(
+ userId = USER_ID,
+ initialized = true,
+ backgroundColor = Color.DKGRAY,
+ app = APP_NAME,
+ appIcon = null,
+ artist = SESSION_ARTIST,
+ song = SESSION_TITLE,
+ artwork = null,
+ actions = emptyList(),
+ actionsToShowInCompact = emptyList(),
+ packageName = PACKAGE,
+ token = null,
+ clickIntent = null,
+ device = null,
+ active = true,
+ resumeAction = null,
+ resumption = false,
+ notificationKey = NOTIF_KEY,
+ hasCheckedForResume = false
+)
+
+private fun <T> eq(value: T): T = Mockito.eq(value) ?: value
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+public class MediaSessionBasedFilterTest : SysuiTestCase() {
+
+ @JvmField @Rule val mockito = MockitoJUnit.rule()
+
+ // Unit to be tested
+ private lateinit var filter: MediaSessionBasedFilter
+
+ private lateinit var sessionListener: MediaSessionManager.OnActiveSessionsChangedListener
+ @Mock private lateinit var mediaListener: MediaDataManager.Listener
+
+ // MediaSessionBasedFilter dependencies
+ @Mock private lateinit var mediaSessionManager: MediaSessionManager
+ private lateinit var fgExecutor: FakeExecutor
+ private lateinit var bgExecutor: FakeExecutor
+
+ @Mock private lateinit var controller1: MediaController
+ @Mock private lateinit var controller2: MediaController
+ @Mock private lateinit var controller3: MediaController
+ @Mock private lateinit var controller4: MediaController
+
+ private lateinit var token1: MediaSession.Token
+ private lateinit var token2: MediaSession.Token
+ private lateinit var token3: MediaSession.Token
+ private lateinit var token4: MediaSession.Token
+
+ @Mock private lateinit var remotePlaybackInfo: PlaybackInfo
+ @Mock private lateinit var localPlaybackInfo: PlaybackInfo
+
+ private lateinit var session1: MediaSession
+ private lateinit var session2: MediaSession
+ private lateinit var session3: MediaSession
+ private lateinit var session4: MediaSession
+
+ private lateinit var mediaData1: MediaData
+ private lateinit var mediaData2: MediaData
+ private lateinit var mediaData3: MediaData
+ private lateinit var mediaData4: MediaData
+
+ @Before
+ fun setUp() {
+ fgExecutor = FakeExecutor(FakeSystemClock())
+ bgExecutor = FakeExecutor(FakeSystemClock())
+ filter = MediaSessionBasedFilter(context, mediaSessionManager, fgExecutor, bgExecutor)
+
+ // Configure mocks.
+ whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(emptyList())
+
+ session1 = MediaSession(context, "MediaSessionBasedFilter1")
+ session2 = MediaSession(context, "MediaSessionBasedFilter2")
+ session3 = MediaSession(context, "MediaSessionBasedFilter3")
+ session4 = MediaSession(context, "MediaSessionBasedFilter4")
+
+ token1 = session1.sessionToken
+ token2 = session2.sessionToken
+ token3 = session3.sessionToken
+ token4 = session4.sessionToken
+
+ whenever(controller1.getSessionToken()).thenReturn(token1)
+ whenever(controller2.getSessionToken()).thenReturn(token2)
+ whenever(controller3.getSessionToken()).thenReturn(token3)
+ whenever(controller4.getSessionToken()).thenReturn(token4)
+
+ whenever(controller1.getPackageName()).thenReturn(PACKAGE)
+ whenever(controller2.getPackageName()).thenReturn(PACKAGE)
+ whenever(controller3.getPackageName()).thenReturn(PACKAGE)
+ whenever(controller4.getPackageName()).thenReturn(PACKAGE)
+
+ mediaData1 = info.copy(token = token1)
+ mediaData2 = info.copy(token = token2)
+ mediaData3 = info.copy(token = token3)
+ mediaData4 = info.copy(token = token4)
+
+ whenever(remotePlaybackInfo.getPlaybackType()).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
+ whenever(localPlaybackInfo.getPlaybackType()).thenReturn(PlaybackInfo.PLAYBACK_TYPE_LOCAL)
+
+ whenever(controller1.getPlaybackInfo()).thenReturn(localPlaybackInfo)
+ whenever(controller2.getPlaybackInfo()).thenReturn(localPlaybackInfo)
+ whenever(controller3.getPlaybackInfo()).thenReturn(localPlaybackInfo)
+ whenever(controller4.getPlaybackInfo()).thenReturn(localPlaybackInfo)
+
+ // Capture listener
+ bgExecutor.runAllReady()
+ val listenerCaptor = ArgumentCaptor.forClass(
+ MediaSessionManager.OnActiveSessionsChangedListener::class.java)
+ verify(mediaSessionManager).addOnActiveSessionsChangedListener(
+ listenerCaptor.capture(), any())
+ sessionListener = listenerCaptor.value
+
+ filter.addListener(mediaListener)
+ }
+
+ @After
+ fun tearDown() {
+ session1.release()
+ session2.release()
+ session3.release()
+ session4.release()
+ }
+
+ @Test
+ fun noMediaSession_loadedEventNotFiltered() {
+ filter.onMediaDataLoaded(KEY, null, mediaData1)
+ bgExecutor.runAllReady()
+ fgExecutor.runAllReady()
+ verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1))
+ }
+
+ @Test
+ fun noMediaSession_removedEventNotFiltered() {
+ filter.onMediaDataRemoved(KEY)
+ bgExecutor.runAllReady()
+ fgExecutor.runAllReady()
+ verify(mediaListener).onMediaDataRemoved(eq(KEY))
+ }
+
+ @Test
+ fun matchingMediaSession_loadedEventNotFiltered() {
+ // GIVEN an active session
+ val controllers = listOf(controller1)
+ whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers)
+ sessionListener.onActiveSessionsChanged(controllers)
+ // WHEN a loaded event is received that matches the session
+ filter.onMediaDataLoaded(KEY, null, mediaData1)
+ bgExecutor.runAllReady()
+ fgExecutor.runAllReady()
+ // THEN the event is not filtered
+ verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1))
+ }
+
+ @Test
+ fun matchingMediaSession_removedEventNotFiltered() {
+ // GIVEN an active session
+ val controllers = listOf(controller1)
+ whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers)
+ sessionListener.onActiveSessionsChanged(controllers)
+ // WHEN a removed event is received
+ filter.onMediaDataRemoved(KEY)
+ bgExecutor.runAllReady()
+ fgExecutor.runAllReady()
+ // THEN the event is not filtered
+ verify(mediaListener).onMediaDataRemoved(eq(KEY))
+ }
+
+ @Test
+ fun remoteSession_loadedEventNotFiltered() {
+ // GIVEN a remove session
+ whenever(controller1.getPlaybackInfo()).thenReturn(remotePlaybackInfo)
+ val controllers = listOf(controller1)
+ whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers)
+ sessionListener.onActiveSessionsChanged(controllers)
+ // WHEN a loaded event is received that matche the session
+ filter.onMediaDataLoaded(KEY, null, mediaData1)
+ bgExecutor.runAllReady()
+ fgExecutor.runAllReady()
+ // THEN the event is not filtered
+ verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1))
+ }
+
+ @Test
+ fun remoteAndLocalSessions_localLoadedEventFiltered() {
+ // GIVEN remote and local sessions
+ whenever(controller1.getPlaybackInfo()).thenReturn(remotePlaybackInfo)
+ val controllers = listOf(controller1, controller2)
+ whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers)
+ sessionListener.onActiveSessionsChanged(controllers)
+ // WHEN a loaded event is received that matches the remote session
+ filter.onMediaDataLoaded(KEY, null, mediaData1)
+ bgExecutor.runAllReady()
+ fgExecutor.runAllReady()
+ // THEN the event is not filtered
+ verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1))
+ // WHEN a loaded event is received that matches the local session
+ filter.onMediaDataLoaded(KEY, null, mediaData2)
+ bgExecutor.runAllReady()
+ fgExecutor.runAllReady()
+ // THEN the event is filtered
+ verify(mediaListener, never()).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData2))
+ }
+
+ @Test
+ fun remoteAndLocalHaveDifferentKeys_localLoadedEventFiltered() {
+ // GIVEN remote and local sessions
+ val key1 = "KEY_1"
+ val key2 = "KEY_2"
+ whenever(controller1.getPlaybackInfo()).thenReturn(remotePlaybackInfo)
+ val controllers = listOf(controller1, controller2)
+ whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers)
+ sessionListener.onActiveSessionsChanged(controllers)
+ // WHEN a loaded event is received that matches the remote session
+ filter.onMediaDataLoaded(key1, null, mediaData1)
+ bgExecutor.runAllReady()
+ fgExecutor.runAllReady()
+ // THEN the event is not filtered
+ verify(mediaListener).onMediaDataLoaded(eq(key1), eq(null), eq(mediaData1))
+ // WHEN a loaded event is received that matches the local session
+ filter.onMediaDataLoaded(key2, null, mediaData2)
+ bgExecutor.runAllReady()
+ fgExecutor.runAllReady()
+ // THEN the event is filtered
+ verify(mediaListener, never()).onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2))
+ // AND there should be a removed event for key2
+ verify(mediaListener).onMediaDataRemoved(eq(key2))
+ }
+
+ @Test
+ fun multipleRemoteSessions_loadedEventNotFiltered() {
+ // GIVEN two remote sessions
+ whenever(controller1.getPlaybackInfo()).thenReturn(remotePlaybackInfo)
+ whenever(controller2.getPlaybackInfo()).thenReturn(remotePlaybackInfo)
+ val controllers = listOf(controller1, controller2)
+ whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers)
+ sessionListener.onActiveSessionsChanged(controllers)
+ // WHEN a loaded event is received that matches the remote session
+ filter.onMediaDataLoaded(KEY, null, mediaData1)
+ bgExecutor.runAllReady()
+ fgExecutor.runAllReady()
+ // THEN the event is not filtered
+ verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1))
+ // WHEN a loaded event is received that matches the local session
+ filter.onMediaDataLoaded(KEY, null, mediaData2)
+ bgExecutor.runAllReady()
+ fgExecutor.runAllReady()
+ // THEN the event is not filtered
+ verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData2))
+ }
+
+ @Test
+ fun multipleOtherSessions_loadedEventNotFiltered() {
+ // GIVEN multiple active sessions from other packages
+ val controllers = listOf(controller1, controller2, controller3, controller4)
+ whenever(controller1.getPackageName()).thenReturn("PKG_1")
+ whenever(controller2.getPackageName()).thenReturn("PKG_2")
+ whenever(controller3.getPackageName()).thenReturn("PKG_3")
+ whenever(controller4.getPackageName()).thenReturn("PKG_4")
+ whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers)
+ sessionListener.onActiveSessionsChanged(controllers)
+ // WHEN a loaded event is received
+ filter.onMediaDataLoaded(KEY, null, mediaData1)
+ bgExecutor.runAllReady()
+ fgExecutor.runAllReady()
+ // THEN the event is not filtered
+ verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1))
+ }
+
+ @Test
+ fun doNotFilterDuringKeyMigration() {
+ val key1 = "KEY_1"
+ val key2 = "KEY_2"
+ // GIVEN a loaded event
+ filter.onMediaDataLoaded(key1, null, mediaData2)
+ bgExecutor.runAllReady()
+ fgExecutor.runAllReady()
+ reset(mediaListener)
+ // GIVEN remote and local sessions
+ whenever(controller1.getPlaybackInfo()).thenReturn(remotePlaybackInfo)
+ val controllers = listOf(controller1, controller2)
+ whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers)
+ sessionListener.onActiveSessionsChanged(controllers)
+ // WHEN a loaded event is received that matches the local session but it is a key migration
+ filter.onMediaDataLoaded(key2, key1, mediaData2)
+ bgExecutor.runAllReady()
+ fgExecutor.runAllReady()
+ // THEN the key migration event is fired
+ verify(mediaListener).onMediaDataLoaded(eq(key2), eq(key1), eq(mediaData2))
+ }
+
+ @Test
+ fun filterAfterKeyMigration() {
+ val key1 = "KEY_1"
+ val key2 = "KEY_2"
+ // GIVEN a loaded event
+ filter.onMediaDataLoaded(key1, null, mediaData1)
+ filter.onMediaDataLoaded(key1, null, mediaData2)
+ bgExecutor.runAllReady()
+ fgExecutor.runAllReady()
+ reset(mediaListener)
+ // GIVEN remote and local sessions
+ whenever(controller1.getPlaybackInfo()).thenReturn(remotePlaybackInfo)
+ val controllers = listOf(controller1, controller2)
+ whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers)
+ sessionListener.onActiveSessionsChanged(controllers)
+ // GIVEN that the keys have been migrated
+ filter.onMediaDataLoaded(key2, key1, mediaData1)
+ filter.onMediaDataLoaded(key2, key1, mediaData2)
+ bgExecutor.runAllReady()
+ fgExecutor.runAllReady()
+ reset(mediaListener)
+ // WHEN a loaded event is received that matches the local session
+ filter.onMediaDataLoaded(key2, null, mediaData2)
+ bgExecutor.runAllReady()
+ fgExecutor.runAllReady()
+ // THEN the key migration event is filtered
+ verify(mediaListener, never()).onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2))
+ // WHEN a loaded event is received that matches the remote session
+ filter.onMediaDataLoaded(key2, null, mediaData1)
+ bgExecutor.runAllReady()
+ fgExecutor.runAllReady()
+ // THEN the key migration event is fired
+ verify(mediaListener).onMediaDataLoaded(eq(key2), eq(null), eq(mediaData1))
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
index 644ed3d..8089561 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
@@ -34,7 +34,6 @@
import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
index 56df193..a36a4c4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -29,7 +29,6 @@
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.phone.DozeParameters
-import com.android.systemui.statusbar.phone.NotificationShadeWindowController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.mockito.eq
import org.junit.Before
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index 6f46923..c35ada7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -212,19 +212,6 @@
}
@Test
- public void testUpdateNotificationViews_appOps() throws Exception {
- NotificationEntry entry0 = createEntry();
- entry0.setRow(spy(entry0.getRow()));
- when(mEntryManager.getVisibleNotifications()).thenReturn(
- Lists.newArrayList(entry0));
- mListContainer.addContainerView(entry0.getRow());
-
- mViewHierarchyManager.updateNotificationViews();
-
- verify(entry0.getRow(), times(1)).showAppOpsIcons(any());
- }
-
- @Test
public void testReentrantCallsToOnDynamicPrivacyChangedPostForLater() {
// GIVEN a ListContainer that will make a re-entrant call to updateNotificationViews()
mMadeReentrantCall = false;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java
index 960ea79..ce8ce2e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java
@@ -77,8 +77,6 @@
private NotificationEntryBuilder mEntryBuilder;
private AppOpsCoordinator mAppOpsCoordinator;
private NotifFilter mForegroundFilter;
- private NotifCollectionListener mNotifCollectionListener;
- private AppOpsController.Callback mAppOpsCallback;
private NotifLifetimeExtender mForegroundNotifLifetimeExtender;
private NotifSection mFgsSection;
@@ -113,19 +111,6 @@
lifetimeExtenderCaptor.capture());
mForegroundNotifLifetimeExtender = lifetimeExtenderCaptor.getValue();
- // capture notifCollectionListener
- ArgumentCaptor<NotifCollectionListener> notifCollectionCaptor =
- ArgumentCaptor.forClass(NotifCollectionListener.class);
- verify(mNotifPipeline, times(1)).addCollectionListener(
- notifCollectionCaptor.capture());
- mNotifCollectionListener = notifCollectionCaptor.getValue();
-
- // capture app ops callback
- ArgumentCaptor<AppOpsController.Callback> appOpsCaptor =
- ArgumentCaptor.forClass(AppOpsController.Callback.class);
- verify(mAppOpsController).addCallback(any(int[].class), appOpsCaptor.capture());
- mAppOpsCallback = appOpsCaptor.getValue();
-
mFgsSection = mAppOpsCoordinator.getSection();
}
@@ -230,136 +215,6 @@
}
@Test
- public void testAppOpsUpdateOnlyAppliedToRelevantNotificationWithStandardLayout() {
- // GIVEN three current notifications, two with the same key but from different users
- NotificationEntry entry1 = new NotificationEntryBuilder()
- .setUser(new UserHandle(NOTIF_USER_ID))
- .setPkg(TEST_PKG)
- .setId(1)
- .build();
- NotificationEntry entry2 = new NotificationEntryBuilder()
- .setUser(new UserHandle(NOTIF_USER_ID))
- .setPkg(TEST_PKG)
- .setId(2)
- .build();
- NotificationEntry entry3_diffUser = new NotificationEntryBuilder()
- .setUser(new UserHandle(NOTIF_USER_ID + 1))
- .setPkg(TEST_PKG)
- .setId(2)
- .build();
- when(mNotifPipeline.getAllNotifs()).thenReturn(List.of(entry1, entry2, entry3_diffUser));
-
- // GIVEN that only entry2 has a standard layout
- when(mForegroundServiceController.getStandardLayoutKeys(NOTIF_USER_ID, TEST_PKG))
- .thenReturn(new ArraySet<>(List.of(entry2.getKey())));
-
- // WHEN a new app ops code comes in
- mAppOpsCallback.onActiveStateChanged(47, NOTIF_USER_ID, TEST_PKG, true);
- mExecutor.runAllReady();
-
- // THEN entry2's app ops are updated, but no one else's are
- assertEquals(
- new ArraySet<>(),
- entry1.mActiveAppOps);
- assertEquals(
- new ArraySet<>(List.of(47)),
- entry2.mActiveAppOps);
- assertEquals(
- new ArraySet<>(),
- entry3_diffUser.mActiveAppOps);
- }
-
- @Test
- public void testAppOpsUpdateAppliedToAllNotificationsWithStandardLayouts() {
- // GIVEN three notifications with standard layouts
- NotificationEntry entry1 = new NotificationEntryBuilder()
- .setUser(new UserHandle(NOTIF_USER_ID))
- .setPkg(TEST_PKG)
- .setId(1)
- .build();
- NotificationEntry entry2 = new NotificationEntryBuilder()
- .setUser(new UserHandle(NOTIF_USER_ID))
- .setPkg(TEST_PKG)
- .setId(2)
- .build();
- NotificationEntry entry3 = new NotificationEntryBuilder()
- .setUser(new UserHandle(NOTIF_USER_ID))
- .setPkg(TEST_PKG)
- .setId(3)
- .build();
- when(mNotifPipeline.getAllNotifs()).thenReturn(List.of(entry1, entry2, entry3));
- when(mForegroundServiceController.getStandardLayoutKeys(NOTIF_USER_ID, TEST_PKG))
- .thenReturn(new ArraySet<>(List.of(entry1.getKey(), entry2.getKey(),
- entry3.getKey())));
-
- // WHEN a new app ops code comes in
- mAppOpsCallback.onActiveStateChanged(47, NOTIF_USER_ID, TEST_PKG, true);
- mExecutor.runAllReady();
-
- // THEN all entries get updated
- assertEquals(
- new ArraySet<>(List.of(47)),
- entry1.mActiveAppOps);
- assertEquals(
- new ArraySet<>(List.of(47)),
- entry2.mActiveAppOps);
- assertEquals(
- new ArraySet<>(List.of(47)),
- entry3.mActiveAppOps);
- }
-
- @Test
- public void testAppOpsAreRemoved() {
- // GIVEN One notification which is associated with app ops
- NotificationEntry entry = new NotificationEntryBuilder()
- .setUser(new UserHandle(NOTIF_USER_ID))
- .setPkg(TEST_PKG)
- .setId(2)
- .build();
- when(mNotifPipeline.getAllNotifs()).thenReturn(List.of(entry));
- when(mForegroundServiceController.getStandardLayoutKeys(0, TEST_PKG))
- .thenReturn(new ArraySet<>(List.of(entry.getKey())));
-
- // GIVEN that the notification's app ops are already [47, 33]
- mAppOpsCallback.onActiveStateChanged(47, NOTIF_USER_ID, TEST_PKG, true);
- mAppOpsCallback.onActiveStateChanged(33, NOTIF_USER_ID, TEST_PKG, true);
- mExecutor.runAllReady();
- assertEquals(
- new ArraySet<>(List.of(47, 33)),
- entry.mActiveAppOps);
-
- // WHEN one of the app ops is removed
- mAppOpsCallback.onActiveStateChanged(47, NOTIF_USER_ID, TEST_PKG, false);
- mExecutor.runAllReady();
-
- // THEN the entry's active app ops are updated as well
- assertEquals(
- new ArraySet<>(List.of(33)),
- entry.mActiveAppOps);
- }
-
- @Test
- public void testNullAppOps() {
- // GIVEN one notification with app ops
- NotificationEntry entry = new NotificationEntryBuilder()
- .setUser(new UserHandle(NOTIF_USER_ID))
- .setPkg(TEST_PKG)
- .setId(2)
- .build();
- entry.mActiveAppOps.clear();
- entry.mActiveAppOps.addAll(List.of(47, 33));
-
- // WHEN the notification is updated and the foreground service controller returns null for
- // this notification
- when(mForegroundServiceController.getAppOps(entry.getSbn().getUser().getIdentifier(),
- entry.getSbn().getPackageName())).thenReturn(null);
- mNotifCollectionListener.onEntryUpdated(entry);
-
- // THEN the entry's active app ops is updated to empty
- assertTrue(entry.mActiveAppOps.isEmpty());
- }
-
- @Test
public void testIncludeFGSInSection_importanceDefault() {
// GIVEN the notification represents a colorized foreground service with > min importance
mEntryBuilder
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/AppOpsInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/AppOpsInfoTest.java
deleted file mode 100644
index 43d8b50..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/AppOpsInfoTest.java
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.notification.row;
-
-import static android.app.AppOpsManager.OP_CAMERA;
-import static android.app.AppOpsManager.OP_RECORD_AUDIO;
-import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertTrue;
-
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.anyString;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.Notification;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.graphics.drawable.Drawable;
-import android.os.UserHandle;
-import android.service.notification.StatusBarNotification;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.UiThreadTest;
-import android.util.ArraySet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.internal.logging.testing.UiEventLoggerFake;
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.concurrent.CountDownLatch;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@UiThreadTest
-public class AppOpsInfoTest extends SysuiTestCase {
- private static final String TEST_PACKAGE_NAME = "test_package";
- private static final int TEST_UID = 1;
-
- private AppOpsInfo mAppOpsInfo;
- private final PackageManager mMockPackageManager = mock(PackageManager.class);
- private final NotificationGuts mGutsParent = mock(NotificationGuts.class);
- private StatusBarNotification mSbn;
- private UiEventLoggerFake mUiEventLogger = new UiEventLoggerFake();
-
- @Before
- public void setUp() throws Exception {
- // Inflate the layout
- final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
- mAppOpsInfo = (AppOpsInfo) layoutInflater.inflate(R.layout.app_ops_info, null);
- mAppOpsInfo.setGutsParent(mGutsParent);
-
- // PackageManager must return a packageInfo and applicationInfo.
- final PackageInfo packageInfo = new PackageInfo();
- packageInfo.packageName = TEST_PACKAGE_NAME;
- when(mMockPackageManager.getPackageInfo(eq(TEST_PACKAGE_NAME), anyInt()))
- .thenReturn(packageInfo);
- final ApplicationInfo applicationInfo = new ApplicationInfo();
- applicationInfo.uid = TEST_UID; // non-zero
- when(mMockPackageManager.getApplicationInfo(anyString(), anyInt())).thenReturn(
- applicationInfo);
-
- mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0,
- new Notification(), UserHandle.CURRENT, null, 0);
- }
-
- @Test
- public void testBindNotification_SetsTextApplicationName() {
- when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
- mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, new ArraySet<>());
- final TextView textView = mAppOpsInfo.findViewById(R.id.pkgname);
- assertTrue(textView.getText().toString().contains("App Name"));
- }
-
- @Test
- public void testBindNotification_SetsPackageIcon() {
- final Drawable iconDrawable = mock(Drawable.class);
- when(mMockPackageManager.getApplicationIcon(any(ApplicationInfo.class)))
- .thenReturn(iconDrawable);
- mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, new ArraySet<>());
- final ImageView iconView = mAppOpsInfo.findViewById(R.id.pkgicon);
- assertEquals(iconDrawable, iconView.getDrawable());
- }
-
- @Test
- public void testBindNotification_SetsOnClickListenerForSettings() throws Exception {
- ArraySet<Integer> expectedOps = new ArraySet<>();
- expectedOps.add(OP_CAMERA);
- final CountDownLatch latch = new CountDownLatch(1);
- mAppOpsInfo.bindGuts(mMockPackageManager, (View v, String pkg, int uid,
- ArraySet<Integer> ops) -> {
- assertEquals(TEST_PACKAGE_NAME, pkg);
- assertEquals(expectedOps, ops);
- assertEquals(TEST_UID, uid);
- latch.countDown();
- }, mSbn, mUiEventLogger, expectedOps);
-
- final View settingsButton = mAppOpsInfo.findViewById(R.id.settings);
- settingsButton.performClick();
- // Verify that listener was triggered.
- assertEquals(0, latch.getCount());
- }
-
- @Test
- public void testBindNotification_LogsOpen() throws Exception {
- mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, new ArraySet<>());
- assertEquals(1, mUiEventLogger.numLogs());
- assertEquals(NotificationAppOpsEvent.NOTIFICATION_APP_OPS_OPEN.getId(),
- mUiEventLogger.eventId(0));
- }
-
- @Test
- public void testOk() {
- ArraySet<Integer> expectedOps = new ArraySet<>();
- expectedOps.add(OP_CAMERA);
- final CountDownLatch latch = new CountDownLatch(1);
- mAppOpsInfo.bindGuts(mMockPackageManager, (View v, String pkg, int uid,
- ArraySet<Integer> ops) -> {
- assertEquals(TEST_PACKAGE_NAME, pkg);
- assertEquals(expectedOps, ops);
- assertEquals(TEST_UID, uid);
- latch.countDown();
- }, mSbn, mUiEventLogger, expectedOps);
-
- final View okButton = mAppOpsInfo.findViewById(R.id.ok);
- okButton.performClick();
- assertEquals(1, latch.getCount());
- verify(mGutsParent, times(1)).closeControls(eq(okButton), anyBoolean());
- }
-
- @Test
- public void testPrompt_camera() {
- ArraySet<Integer> expectedOps = new ArraySet<>();
- expectedOps.add(OP_CAMERA);
- mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps);
- TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
- assertEquals("This app is using the camera.", prompt.getText());
- }
-
- @Test
- public void testPrompt_mic() {
- ArraySet<Integer> expectedOps = new ArraySet<>();
- expectedOps.add(OP_RECORD_AUDIO);
- mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps);
- TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
- assertEquals("This app is using the microphone.", prompt.getText());
- }
-
- @Test
- public void testPrompt_overlay() {
- ArraySet<Integer> expectedOps = new ArraySet<>();
- expectedOps.add(OP_SYSTEM_ALERT_WINDOW);
- mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps);
- TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
- assertEquals("This app is displaying over other apps on your screen.", prompt.getText());
- }
-
- @Test
- public void testPrompt_camera_mic() {
- ArraySet<Integer> expectedOps = new ArraySet<>();
- expectedOps.add(OP_CAMERA);
- expectedOps.add(OP_RECORD_AUDIO);
- mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps);
- TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
- assertEquals("This app is using the microphone and camera.", prompt.getText());
- }
-
- @Test
- public void testPrompt_camera_mic_overlay() {
- ArraySet<Integer> expectedOps = new ArraySet<>();
- expectedOps.add(OP_CAMERA);
- expectedOps.add(OP_RECORD_AUDIO);
- expectedOps.add(OP_SYSTEM_ALERT_WINDOW);
- mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps);
- TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
- assertEquals("This app is displaying over other apps on your screen and using"
- + " the microphone and camera.", prompt.getText());
- }
-
- @Test
- public void testPrompt_camera_overlay() {
- ArraySet<Integer> expectedOps = new ArraySet<>();
- expectedOps.add(OP_CAMERA);
- expectedOps.add(OP_SYSTEM_ALERT_WINDOW);
- mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps);
- TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
- assertEquals("This app is displaying over other apps on your screen and using"
- + " the camera.", prompt.getText());
- }
-
- @Test
- public void testPrompt_mic_overlay() {
- ArraySet<Integer> expectedOps = new ArraySet<>();
- expectedOps.add(OP_RECORD_AUDIO);
- expectedOps.add(OP_SYSTEM_ALERT_WINDOW);
- mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps);
- TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
- assertEquals("This app is displaying over other apps on your screen and using"
- + " the microphone.", prompt.getText());
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index dc4a6ca..f29b46c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -35,12 +35,10 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.app.AppOpsManager;
import android.app.NotificationChannel;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
-import android.util.ArraySet;
import android.view.View;
import androidx.test.filters.SmallTest;
@@ -213,46 +211,6 @@
}
@Test
- public void testShowAppOps_noHeader() {
- // public notification is custom layout - no header
- mGroupRow.setSensitive(true, true);
- mGroupRow.setAppOpsOnClickListener(null);
- mGroupRow.showAppOpsIcons(null);
- }
-
- @Test
- public void testShowAppOpsIcons_header() {
- NotificationContentView publicLayout = mock(NotificationContentView.class);
- mGroupRow.setPublicLayout(publicLayout);
- NotificationContentView privateLayout = mock(NotificationContentView.class);
- mGroupRow.setPrivateLayout(privateLayout);
- NotificationChildrenContainer mockContainer = mock(NotificationChildrenContainer.class);
- when(mockContainer.getNotificationChildCount()).thenReturn(1);
- mGroupRow.setChildrenContainer(mockContainer);
-
- ArraySet<Integer> ops = new ArraySet<>();
- ops.add(AppOpsManager.OP_ANSWER_PHONE_CALLS);
- mGroupRow.showAppOpsIcons(ops);
-
- verify(mockContainer, times(1)).showAppOpsIcons(ops);
- verify(privateLayout, times(1)).showAppOpsIcons(ops);
- verify(publicLayout, times(1)).showAppOpsIcons(ops);
-
- }
-
- @Test
- public void testAppOpsOnClick() {
- ExpandableNotificationRow.CoordinateOnClickListener l = mock(
- ExpandableNotificationRow.CoordinateOnClickListener.class);
- View view = mock(View.class);
-
- mGroupRow.setAppOpsOnClickListener(l);
-
- mGroupRow.getAppOpsOnClickListener().onClick(view);
- verify(l, times(1)).onClick(any(), anyInt(), anyInt(), any());
- }
-
- @Test
public void testFeedback_noHeader() {
// public notification is custom layout - no header
mGroupRow.setSensitive(true, true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
index 6d4a711..c2091da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
@@ -76,32 +76,6 @@
@Test
@UiThreadTest
- public void testShowAppOpsIcons() {
- View mockContracted = mock(NotificationHeaderView.class);
- when(mockContracted.findViewById(com.android.internal.R.id.mic))
- .thenReturn(mockContracted);
- View mockExpanded = mock(NotificationHeaderView.class);
- when(mockExpanded.findViewById(com.android.internal.R.id.mic))
- .thenReturn(mockExpanded);
- View mockHeadsUp = mock(NotificationHeaderView.class);
- when(mockHeadsUp.findViewById(com.android.internal.R.id.mic))
- .thenReturn(mockHeadsUp);
-
- mView.setContractedChild(mockContracted);
- mView.setExpandedChild(mockExpanded);
- mView.setHeadsUpChild(mockHeadsUp);
-
- ArraySet<Integer> ops = new ArraySet<>();
- ops.add(AppOpsManager.OP_RECORD_AUDIO);
- mView.showAppOpsIcons(ops);
-
- verify(mockContracted, times(1)).setVisibility(View.VISIBLE);
- verify(mockExpanded, times(1)).setVisibility(View.VISIBLE);
- verify(mockHeadsUp, times(1)).setVisibility(View.VISIBLE);
- }
-
- @Test
- @UiThreadTest
public void testShowFeedbackIcon() {
View mockContracted = mock(NotificationHeaderView.class);
when(mockContracted.findViewById(com.android.internal.R.id.feedback))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 8ccbb2e..fa2fa04 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -52,6 +52,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.notification.ConversationNotificationProcessor;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -68,7 +69,6 @@
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.policy.SmartReplyConstants;
import org.mockito.ArgumentCaptor;
@@ -422,7 +422,6 @@
mock(OnExpandClickListener.class),
mock(NotificationMediaManager.class),
mock(ExpandableNotificationRow.CoordinateOnClickListener.class),
- mock(ExpandableNotificationRow.CoordinateOnClickListener.class),
mock(FalsingManager.class),
mStatusBarStateController,
mPeopleNotificationIdentifier);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 64907ee..f1c8ece 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -43,6 +43,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
@@ -76,7 +77,7 @@
@Mock
private ScrimController mScrimController;
@Mock
- private StatusBar mStatusBar;
+ private BiometricUnlockController.BiometricModeListener mBiometricModeListener;
@Mock
private ShadeController mShadeController;
@Mock
@@ -105,11 +106,12 @@
mDependency.injectTestDependency(NotificationMediaManager.class, mMediaManager);
res.addOverride(com.android.internal.R.integer.config_wakeUpDelayDoze, 0);
mBiometricUnlockController = new BiometricUnlockController(mContext, mDozeScrimController,
- mKeyguardViewMediator, mScrimController, mStatusBar, mShadeController,
+ mKeyguardViewMediator, mScrimController, mShadeController,
mNotificationShadeWindowController, mKeyguardStateController, mHandler,
mUpdateMonitor, res.getResources(), mKeyguardBypassController, mDozeParameters,
mMetricsLogger, mDumpManager);
mBiometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager);
+ mBiometricUnlockController.setBiometricModeListener(mBiometricModeListener);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
index a6e2918..a5f4e51 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
@@ -40,6 +40,7 @@
import com.android.systemui.doze.DozeLog;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index 6d642ec..48bf2db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -33,6 +33,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.AlertingNotificationManager;
import com.android.systemui.statusbar.AlertingNotificationManagerTest;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java
index 8c37cf1..fcea17c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java
@@ -57,7 +57,7 @@
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@SmallTest
-public class NotificationShadeWindowControllerTest extends SysuiTestCase {
+public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
@Mock private WindowManager mWindowManager;
@Mock private DozeParameters mDozeParameters;
@@ -72,7 +72,7 @@
@Mock private DumpManager mDumpManager;
@Captor private ArgumentCaptor<WindowManager.LayoutParams> mLayoutParameters;
- private NotificationShadeWindowController mNotificationShadeWindowController;
+ private NotificationShadeWindowControllerImpl mNotificationShadeWindowController;
@Before
public void setUp() {
@@ -80,7 +80,7 @@
when(mDozeParameters.getAlwaysOn()).thenReturn(true);
when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors);
- mNotificationShadeWindowController = new NotificationShadeWindowController(mContext,
+ mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext,
mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController,
mColorExtractor, mDumpManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
index 51900cb..ea57cc9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
@@ -38,6 +38,7 @@
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationShadeDepthController;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.SuperStatusBarViewFactory;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 50d891e..ccc3078 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -47,6 +47,7 @@
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index e4f4812..e46aa04 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -43,6 +43,7 @@
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.NotificationViewHierarchyManager;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index aa09406..8a1d95ea4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -104,6 +104,7 @@
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShadeDepthController;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.NotificationViewHierarchyManager;
import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.RemoteInputController;
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 32d02fb..96d973e 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -127,6 +127,7 @@
private final int mPreviousVibrationsLimit;
private final boolean mAllowPriorityVibrationsInLowPowerMode;
private final List<Integer> mSupportedEffects;
+ private final List<Integer> mSupportedPrimitives;
private final long mCapabilities;
private final int mDefaultVibrationAmplitude;
private final SparseArray<VibrationEffect> mFallbackEffects;
@@ -184,6 +185,8 @@
static native int[] vibratorGetSupportedEffects(long controllerPtr);
+ static native int[] vibratorGetSupportedPrimitives(long controllerPtr);
+
static native long vibratorPerformEffect(
long controllerPtr, long effect, long strength, Vibration vibration);
@@ -397,6 +400,7 @@
mNativeWrapper.vibratorOff();
mSupportedEffects = asList(mNativeWrapper.vibratorGetSupportedEffects());
+ mSupportedPrimitives = asList(mNativeWrapper.vibratorGetSupportedPrimitives());
mCapabilities = mNativeWrapper.vibratorGetCapabilities();
mContext = context;
@@ -647,8 +651,11 @@
@Override // Binder call
public boolean[] arePrimitivesSupported(int[] primitiveIds) {
boolean[] supported = new boolean[primitiveIds.length];
- if (hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
- Arrays.fill(supported, true);
+ if (!hasCapability(IVibrator.CAP_COMPOSE_EFFECTS) || mSupportedPrimitives == null) {
+ return supported;
+ }
+ for (int i = 0; i < primitiveIds.length; i++) {
+ supported[i] = mSupportedPrimitives.contains(primitiveIds[i]);
}
return supported;
}
@@ -1501,6 +1508,7 @@
pw.println(" mNotificationIntensity=" + mNotificationIntensity);
pw.println(" mRingIntensity=" + mRingIntensity);
pw.println(" mSupportedEffects=" + mSupportedEffects);
+ pw.println(" mSupportedPrimitives=" + mSupportedPrimitives);
pw.println();
pw.println(" Previous ring vibrations:");
for (VibrationInfo info : mPreviousRingVibrations) {
@@ -1759,6 +1767,11 @@
return VibratorService.vibratorGetSupportedEffects(mNativeControllerPtr);
}
+ /** Returns all compose primitives supported by the device vibrator. */
+ public int[] vibratorGetSupportedPrimitives() {
+ return VibratorService.vibratorGetSupportedPrimitives(mNativeControllerPtr);
+ }
+
/** Turns vibrator on to perform one of the supported effects. */
public long vibratorPerformEffect(long effect, long strength, Vibration vibration) {
return VibratorService.vibratorPerformEffect(
diff --git a/services/core/java/com/android/server/am/UidObserverController.java b/services/core/java/com/android/server/am/UidObserverController.java
index 4c4dd8b..4d9260a 100644
--- a/services/core/java/com/android/server/am/UidObserverController.java
+++ b/services/core/java/com/android/server/am/UidObserverController.java
@@ -86,15 +86,17 @@
}
mService.mUiHandler.post(this::dispatchUidsChanged);
}
- final int NA = mAvailUidChanges.size();
- if (NA > 0) {
- pendingChange = mAvailUidChanges.remove(NA-1);
- if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
- "Retrieving available item: " + pendingChange);
+ final int size = mAvailUidChanges.size();
+ if (size > 0) {
+ pendingChange = mAvailUidChanges.remove(size - 1);
+ if (DEBUG_UID_OBSERVERS) {
+ Slog.i(TAG_UID_OBSERVERS, "Retrieving available item: " + pendingChange);
+ }
} else {
pendingChange = new UidRecord.ChangeItem();
- if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
- "Allocating new item: " + pendingChange);
+ if (DEBUG_UID_OBSERVERS) {
+ Slog.i(TAG_UID_OBSERVERS, "Allocating new item: " + pendingChange);
+ }
}
if (uidRec != null) {
uidRec.pendingChange = pendingChange;
@@ -134,7 +136,8 @@
}
}
pendingChange.change = change;
- pendingChange.processState = uidRec != null ? uidRec.setProcState : PROCESS_STATE_NONEXISTENT;
+ pendingChange.processState = uidRec != null
+ ? uidRec.setProcState : PROCESS_STATE_NONEXISTENT;
pendingChange.capability = uidRec != null ? uidRec.setCapability : 0;
pendingChange.ephemeral = uidRec != null ? uidRec.ephemeral : isEphemeralLocked(uid);
pendingChange.procStateSeq = uidRec != null ? uidRec.curProcStateSeq : 0;
@@ -165,13 +168,13 @@
@VisibleForTesting
void dispatchUidsChanged() {
- int N;
+ int numUidChanges;
synchronized (mService) {
- N = mPendingUidChanges.size();
- if (mActiveUidChanges.length < N) {
- mActiveUidChanges = new UidRecord.ChangeItem[N];
+ numUidChanges = mPendingUidChanges.size();
+ if (mActiveUidChanges.length < numUidChanges) {
+ mActiveUidChanges = new UidRecord.ChangeItem[numUidChanges];
}
- for (int i=0; i<N; i++) {
+ for (int i = 0; i < numUidChanges; i++) {
final UidRecord.ChangeItem change = mPendingUidChanges.get(i);
mActiveUidChanges[i] = change;
if (change.uidRecord != null) {
@@ -180,21 +183,22 @@
}
}
mPendingUidChanges.clear();
- if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
- "*** Delivering " + N + " uid changes");
+ if (DEBUG_UID_OBSERVERS) {
+ Slog.i(TAG_UID_OBSERVERS, "*** Delivering " + numUidChanges + " uid changes");
+ }
}
- mUidChangeDispatchCount += N;
+ mUidChangeDispatchCount += numUidChanges;
int i = mUidObservers.beginBroadcast();
while (i > 0) {
i--;
dispatchUidsChangedForObserver(mUidObservers.getBroadcastItem(i),
- (UidObserverRegistration) mUidObservers.getBroadcastCookie(i), N);
+ (UidObserverRegistration) mUidObservers.getBroadcastCookie(i), numUidChanges);
}
mUidObservers.finishBroadcast();
if (VALIDATE_UID_STATES && mUidObservers.getRegisteredCallbackCount() > 0) {
- for (int j = 0; j < N; ++j) {
+ for (int j = 0; j < numUidChanges; ++j) {
final UidRecord.ChangeItem item = mActiveUidChanges[j];
if ((item.change & UidRecord.CHANGE_GONE) != 0) {
mValidateUids.remove(item.uid);
@@ -217,7 +221,7 @@
}
synchronized (mService) {
- for (int j = 0; j < N; j++) {
+ for (int j = 0; j < numUidChanges; j++) {
mAvailUidChanges.add(mActiveUidChanges[j]);
}
}
@@ -232,66 +236,72 @@
for (int j = 0; j < changesSize; j++) {
UidRecord.ChangeItem item = mActiveUidChanges[j];
final int change = item.change;
- if (change == UidRecord.CHANGE_PROCSTATE &&
- (reg.which & ActivityManager.UID_OBSERVER_PROCSTATE) == 0) {
+ if (change == UidRecord.CHANGE_PROCSTATE
+ && (reg.mWhich & ActivityManager.UID_OBSERVER_PROCSTATE) == 0) {
// No-op common case: no significant change, the observer is not
// interested in all proc state changes.
continue;
}
final long start = SystemClock.uptimeMillis();
if ((change & UidRecord.CHANGE_IDLE) != 0) {
- if ((reg.which & ActivityManager.UID_OBSERVER_IDLE) != 0) {
- if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
- "UID idle uid=" + item.uid);
+ if ((reg.mWhich & ActivityManager.UID_OBSERVER_IDLE) != 0) {
+ if (DEBUG_UID_OBSERVERS) {
+ Slog.i(TAG_UID_OBSERVERS, "UID idle uid=" + item.uid);
+ }
observer.onUidIdle(item.uid, item.ephemeral);
}
} else if ((change & UidRecord.CHANGE_ACTIVE) != 0) {
- if ((reg.which & ActivityManager.UID_OBSERVER_ACTIVE) != 0) {
- if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
- "UID active uid=" + item.uid);
+ if ((reg.mWhich & ActivityManager.UID_OBSERVER_ACTIVE) != 0) {
+ if (DEBUG_UID_OBSERVERS) {
+ Slog.i(TAG_UID_OBSERVERS, "UID active uid=" + item.uid);
+ }
observer.onUidActive(item.uid);
}
}
- if ((reg.which & ActivityManager.UID_OBSERVER_CACHED) != 0) {
+ if ((reg.mWhich & ActivityManager.UID_OBSERVER_CACHED) != 0) {
if ((change & UidRecord.CHANGE_CACHED) != 0) {
- if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
- "UID cached uid=" + item.uid);
+ if (DEBUG_UID_OBSERVERS) {
+ Slog.i(TAG_UID_OBSERVERS, "UID cached uid=" + item.uid);
+ }
observer.onUidCachedChanged(item.uid, true);
} else if ((change & UidRecord.CHANGE_UNCACHED) != 0) {
- if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
- "UID active uid=" + item.uid);
+ if (DEBUG_UID_OBSERVERS) {
+ Slog.i(TAG_UID_OBSERVERS, "UID active uid=" + item.uid);
+ }
observer.onUidCachedChanged(item.uid, false);
}
}
if ((change & UidRecord.CHANGE_GONE) != 0) {
- if ((reg.which & ActivityManager.UID_OBSERVER_GONE) != 0) {
- if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
- "UID gone uid=" + item.uid);
+ if ((reg.mWhich & ActivityManager.UID_OBSERVER_GONE) != 0) {
+ if (DEBUG_UID_OBSERVERS) {
+ Slog.i(TAG_UID_OBSERVERS, "UID gone uid=" + item.uid);
+ }
observer.onUidGone(item.uid, item.ephemeral);
}
- if (reg.lastProcStates != null) {
- reg.lastProcStates.delete(item.uid);
+ if (reg.mLastProcStates != null) {
+ reg.mLastProcStates.delete(item.uid);
}
} else {
- if ((reg.which & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) {
- if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
- "UID CHANGED uid=" + item.uid
- + ": " + item.processState + ": " + item.capability);
+ if ((reg.mWhich & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) {
+ if (DEBUG_UID_OBSERVERS) {
+ Slog.i(TAG_UID_OBSERVERS, "UID CHANGED uid=" + item.uid
+ + ": " + item.processState + ": " + item.capability);
+ }
boolean doReport = true;
- if (reg.cutpoint >= ActivityManager.MIN_PROCESS_STATE) {
- final int lastState = reg.lastProcStates.get(item.uid,
+ if (reg.mCutpoint >= ActivityManager.MIN_PROCESS_STATE) {
+ final int lastState = reg.mLastProcStates.get(item.uid,
ActivityManager.PROCESS_STATE_UNKNOWN);
if (lastState != ActivityManager.PROCESS_STATE_UNKNOWN) {
- final boolean lastAboveCut = lastState <= reg.cutpoint;
- final boolean newAboveCut = item.processState <= reg.cutpoint;
+ final boolean lastAboveCut = lastState <= reg.mCutpoint;
+ final boolean newAboveCut = item.processState <= reg.mCutpoint;
doReport = lastAboveCut != newAboveCut;
} else {
doReport = item.processState != PROCESS_STATE_NONEXISTENT;
}
}
if (doReport) {
- if (reg.lastProcStates != null) {
- reg.lastProcStates.put(item.uid, item.processState);
+ if (reg.mLastProcStates != null) {
+ reg.mLastProcStates.put(item.uid, item.processState);
}
observer.onUidStateChanged(item.uid, item.processState,
item.procStateSeq, item.capability);
@@ -311,7 +321,7 @@
}
private boolean isEphemeralLocked(int uid) {
- String packages[] = mService.mContext.getPackageManager().getPackagesForUid(uid);
+ final String[] packages = mService.mContext.getPackageManager().getPackagesForUid(uid);
if (packages == null || packages.length != 1) { // Ephemeral apps cannot share uid
return false;
}
@@ -321,41 +331,41 @@
@GuardedBy("mService")
void dump(PrintWriter pw, String dumpPackage) {
- final int NI = mUidObservers.getRegisteredCallbackCount();
+ final int count = mUidObservers.getRegisteredCallbackCount();
boolean printed = false;
- for (int i=0; i<NI; i++) {
+ for (int i = 0; i < count; i++) {
final UidObserverRegistration reg = (UidObserverRegistration)
mUidObservers.getRegisteredCallbackCookie(i);
- if (dumpPackage == null || dumpPackage.equals(reg.pkg)) {
+ if (dumpPackage == null || dumpPackage.equals(reg.mPkg)) {
if (!printed) {
pw.println(" mUidObservers:");
printed = true;
}
- pw.print(" "); UserHandle.formatUid(pw, reg.uid);
- pw.print(" "); pw.print(reg.pkg);
+ pw.print(" "); UserHandle.formatUid(pw, reg.mUid);
+ pw.print(" "); pw.print(reg.mPkg);
final IUidObserver observer = mUidObservers.getRegisteredCallbackItem(i);
pw.print(" "); pw.print(observer.getClass().getTypeName()); pw.print(":");
- if ((reg.which&ActivityManager.UID_OBSERVER_IDLE) != 0) {
+ if ((reg.mWhich & ActivityManager.UID_OBSERVER_IDLE) != 0) {
pw.print(" IDLE");
}
- if ((reg.which&ActivityManager.UID_OBSERVER_ACTIVE) != 0) {
- pw.print(" ACT" );
+ if ((reg.mWhich & ActivityManager.UID_OBSERVER_ACTIVE) != 0) {
+ pw.print(" ACT");
}
- if ((reg.which&ActivityManager.UID_OBSERVER_GONE) != 0) {
+ if ((reg.mWhich & ActivityManager.UID_OBSERVER_GONE) != 0) {
pw.print(" GONE");
}
- if ((reg.which&ActivityManager.UID_OBSERVER_PROCSTATE) != 0) {
+ if ((reg.mWhich & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) {
pw.print(" STATE");
- pw.print(" (cut="); pw.print(reg.cutpoint);
+ pw.print(" (cut="); pw.print(reg.mCutpoint);
pw.print(")");
}
pw.println();
- if (reg.lastProcStates != null) {
- final int NJ = reg.lastProcStates.size();
- for (int j=0; j<NJ; j++) {
+ if (reg.mLastProcStates != null) {
+ final int size = reg.mLastProcStates.size();
+ for (int j = 0; j < size; j++) {
pw.print(" Last ");
- UserHandle.formatUid(pw, reg.lastProcStates.keyAt(j));
- pw.print(": "); pw.println(reg.lastProcStates.valueAt(j));
+ UserHandle.formatUid(pw, reg.mLastProcStates.keyAt(j));
+ pw.print(": "); pw.println(reg.mLastProcStates.valueAt(j));
}
}
}
@@ -366,8 +376,8 @@
pw.print(mUidChangeDispatchCount);
pw.println();
pw.println(" Slow UID dispatches:");
- final int N = mUidObservers.beginBroadcast();
- for (int i = 0; i < N; i++) {
+ final int size = mUidObservers.beginBroadcast();
+ for (int i = 0; i < size; i++) {
UidObserverRegistration r =
(UidObserverRegistration) mUidObservers.getBroadcastCookie(i);
pw.print(" ");
@@ -383,21 +393,21 @@
@GuardedBy("mService")
void dumpDebug(ProtoOutputStream proto, String dumpPackage) {
- final int NI = mUidObservers.getRegisteredCallbackCount();
- for (int i=0; i<NI; i++) {
+ final int count = mUidObservers.getRegisteredCallbackCount();
+ for (int i = 0; i < count; i++) {
final UidObserverRegistration reg = (UidObserverRegistration)
mUidObservers.getRegisteredCallbackCookie(i);
- if (dumpPackage == null || dumpPackage.equals(reg.pkg)) {
+ if (dumpPackage == null || dumpPackage.equals(reg.mPkg)) {
reg.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.UID_OBSERVERS);
}
}
}
- static final class UidObserverRegistration {
- final int uid;
- final String pkg;
- final int which;
- final int cutpoint;
+ private static final class UidObserverRegistration {
+ final int mUid;
+ final String mPkg;
+ final int mWhich;
+ final int mCutpoint;
/**
* Total # of callback calls that took more than {@link #SLOW_UID_OBSERVER_THRESHOLD_MS}.
@@ -408,49 +418,49 @@
/** Max time it took for each dispatch. */
int mMaxDispatchTime;
- final SparseIntArray lastProcStates;
+ final SparseIntArray mLastProcStates;
// Please keep the enum lists in sync
- private static int[] ORIG_ENUMS = new int[]{
+ private static final int[] ORIG_ENUMS = new int[]{
ActivityManager.UID_OBSERVER_IDLE,
ActivityManager.UID_OBSERVER_ACTIVE,
ActivityManager.UID_OBSERVER_GONE,
ActivityManager.UID_OBSERVER_PROCSTATE,
};
- private static int[] PROTO_ENUMS = new int[]{
+ private static final int[] PROTO_ENUMS = new int[]{
ActivityManagerProto.UID_OBSERVER_FLAG_IDLE,
ActivityManagerProto.UID_OBSERVER_FLAG_ACTIVE,
ActivityManagerProto.UID_OBSERVER_FLAG_GONE,
ActivityManagerProto.UID_OBSERVER_FLAG_PROCSTATE,
};
- UidObserverRegistration(int _uid, String _pkg, int _which, int _cutpoint) {
- uid = _uid;
- pkg = _pkg;
- which = _which;
- cutpoint = _cutpoint;
+ UidObserverRegistration(int uid, String pkg, int which, int cutpoint) {
+ this.mUid = uid;
+ this.mPkg = pkg;
+ this.mWhich = which;
+ this.mCutpoint = cutpoint;
if (cutpoint >= ActivityManager.MIN_PROCESS_STATE) {
- lastProcStates = new SparseIntArray();
+ mLastProcStates = new SparseIntArray();
} else {
- lastProcStates = null;
+ mLastProcStates = null;
}
}
void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
- proto.write(UidObserverRegistrationProto.UID, uid);
- proto.write(UidObserverRegistrationProto.PACKAGE, pkg);
+ proto.write(UidObserverRegistrationProto.UID, mUid);
+ proto.write(UidObserverRegistrationProto.PACKAGE, mPkg);
ProtoUtils.writeBitWiseFlagsToProtoEnum(proto, UidObserverRegistrationProto.FLAGS,
- which, ORIG_ENUMS, PROTO_ENUMS);
- proto.write(UidObserverRegistrationProto.CUT_POINT, cutpoint);
- if (lastProcStates != null) {
- final int NI = lastProcStates.size();
- for (int i=0; i<NI; i++) {
+ mWhich, ORIG_ENUMS, PROTO_ENUMS);
+ proto.write(UidObserverRegistrationProto.CUT_POINT, mCutpoint);
+ if (mLastProcStates != null) {
+ final int size = mLastProcStates.size();
+ for (int i = 0; i < size; i++) {
final long pToken = proto.start(UidObserverRegistrationProto.LAST_PROC_STATES);
proto.write(UidObserverRegistrationProto.ProcState.UID,
- lastProcStates.keyAt(i));
+ mLastProcStates.keyAt(i));
proto.write(UidObserverRegistrationProto.ProcState.STATE,
- lastProcStates.valueAt(i));
+ mLastProcStates.valueAt(i));
proto.end(pToken);
}
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 295143e..29ee8eb 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -4367,7 +4367,7 @@
// Flip state because app was explicitly added or removed to denylist.
setMeteredNetworkDenylist(uid, (isDenylisted || isRestrictedByAdmin));
if (hasRule(oldRule, RULE_REJECT_METERED) && isAllowlisted) {
- // Since dneylist prevails over allowlist, we need to handle the special case
+ // Since denylist prevails over allowlist, we need to handle the special case
// where app is allowlisted and denylisted at the same time (although such
// scenario should be blocked by the UI), then denylist is removed.
setMeteredNetworkAllowlist(uid, isAllowlisted);
diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp
index 529fb88..b3f3a5e 100644
--- a/services/core/jni/com_android_server_VibratorService.cpp
+++ b/services/core/jni/com_android_server_VibratorService.cpp
@@ -181,6 +181,24 @@
return effects;
}
+static jintArray vibratorGetSupportedPrimitives(JNIEnv* env, jclass /* clazz */,
+ jlong controllerPtr) {
+ vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
+ if (controller == nullptr) {
+ ALOGE("vibratorGetSupportedPrimitives failed because controller was not initialized");
+ return nullptr;
+ }
+ auto result = controller->getSupportedPrimitives();
+ if (!result.isOk()) {
+ return nullptr;
+ }
+ std::vector<aidl::CompositePrimitive> supportedPrimitives = result.value();
+ jintArray primitives = env->NewIntArray(supportedPrimitives.size());
+ env->SetIntArrayRegion(primitives, 0, supportedPrimitives.size(),
+ reinterpret_cast<jint*>(supportedPrimitives.data()));
+ return primitives;
+}
+
static jlong vibratorPerformEffect(JNIEnv* env, jclass /* clazz */, jlong controllerPtr,
jlong effect, jlong strength, jobject vibration) {
vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
@@ -259,6 +277,7 @@
"VibratorService$Vibration;)V",
(void*)vibratorPerformComposedEffect},
{"vibratorGetSupportedEffects", "(J)[I", (void*)vibratorGetSupportedEffects},
+ {"vibratorGetSupportedPrimitives", "(J)[I", (void*)vibratorGetSupportedPrimitives},
{"vibratorSetExternalControl", "(JZ)V", (void*)vibratorSetExternalControl},
{"vibratorGetCapabilities", "(J)J", (void*)vibratorGetCapabilities},
{"vibratorAlwaysOnEnable", "(JJJJ)V", (void*)vibratorAlwaysOnEnable},
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 2ab629b..6154bef 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1579,21 +1579,22 @@
/**
* Creates a new {@link CallerIdentity} object to represent the caller's identity.
+ * The component name should be an active admin for the calling user.
*/
- private CallerIdentity getCallerIdentity(@NonNull ComponentName componentName) {
+ private CallerIdentity getCallerIdentity(@NonNull ComponentName adminComponent) {
final int callerUid = mInjector.binderGetCallingUid();
final DevicePolicyData policy = getUserData(UserHandle.getUserId(callerUid));
- ActiveAdmin admin = policy.mAdminMap.get(componentName);
+ ActiveAdmin admin = policy.mAdminMap.get(adminComponent);
if (admin == null) {
- throw new SecurityException(String.format("No active admin for %s", componentName));
+ throw new SecurityException(String.format("No active admin for %s", adminComponent));
}
if (admin.getUid() != callerUid) {
throw new SecurityException(
- String.format("Admin %s is not owned by uid %d", componentName, callerUid));
+ String.format("Admin %s is not owned by uid %d", adminComponent, callerUid));
}
- return new CallerIdentity(callerUid, componentName.getPackageName(), componentName);
+ return new CallerIdentity(callerUid, adminComponent.getPackageName(), adminComponent);
}
/**
@@ -4589,12 +4590,6 @@
}
}
- private void enforceDeviceOwner(ComponentName who) {
- synchronized (getLockObject()) {
- getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
- }
- }
-
private void enforceProfileOrDeviceOwner(ComponentName who) {
synchronized (getLockObject()) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
@@ -5194,20 +5189,23 @@
Objects.requireNonNull(who, "ComponentName is null");
Preconditions.checkStringNotEmpty(delegatePackage, "Delegate package is null or empty");
Preconditions.checkCollectionElementsNotNull(scopeList, "Scopes");
+ final CallerIdentity identity = getCallerIdentity(who);
+
// Remove possible duplicates.
final ArrayList<String> scopes = new ArrayList(new ArraySet(scopeList));
// Ensure given scopes are valid.
if (scopes.retainAll(Arrays.asList(DELEGATIONS))) {
throw new IllegalArgumentException("Unexpected delegation scopes");
}
- final boolean hasDoDelegation = !Collections.disjoint(scopes, DEVICE_OWNER_DELEGATIONS);
// Retrieve the user ID of the calling process.
- final int userId = mInjector.userHandleGetCallingUserId();
+ final int userId = identity.getUserId();
+ final boolean hasDoDelegation = !Collections.disjoint(scopes, DEVICE_OWNER_DELEGATIONS);
synchronized (getLockObject()) {
// Ensure calling process is device/profile owner.
if (hasDoDelegation) {
- getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
} else {
+ // TODO move whole condition out of synchronized block
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
}
// Ensure the delegate is installed (skip this for DELEGATION_CERT_INSTALL in pre-N).
@@ -6199,7 +6197,9 @@
@Override
public void setRecommendedGlobalProxy(ComponentName who, ProxyInfo proxyInfo) {
- enforceDeviceOwner(who);
+ Objects.requireNonNull(who, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
mInjector.binderWithCleanCallingIdentity(
() -> mInjector.getConnectivityManager().setGlobalProxy(proxyInfo));
}
@@ -6620,6 +6620,9 @@
return;
}
Objects.requireNonNull(who, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
+
// Allow setting this policy to true only if there is a split system user.
if (forceEphemeralUsers && !mInjector.userManagerIsSplitSystemUser()) {
throw new UnsupportedOperationException(
@@ -6627,11 +6630,10 @@
}
boolean removeAllUsers = false;
synchronized (getLockObject()) {
- final ActiveAdmin deviceOwner =
- getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
if (deviceOwner.forceEphemeralUsers != forceEphemeralUsers) {
deviceOwner.forceEphemeralUsers = forceEphemeralUsers;
- saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+ saveSettingsLocked(identity.getUserId());
mUserManagerInternal.setForceEphemeralUsers(forceEphemeralUsers);
removeAllUsers = forceEphemeralUsers;
}
@@ -6647,21 +6649,15 @@
return false;
}
Objects.requireNonNull(who, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
+
synchronized (getLockObject()) {
- final ActiveAdmin deviceOwner =
- getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
return deviceOwner.forceEphemeralUsers;
}
}
- private void ensureDeviceOwnerAndAllUsersAffiliated(ComponentName who)
- throws SecurityException {
- synchronized (getLockObject()) {
- getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
- }
- ensureAllUsersAffiliated();
- }
-
private void ensureAllUsersAffiliated() throws SecurityException {
synchronized (getLockObject()) {
if (!areAllUsersAffiliatedWithDeviceLocked()) {
@@ -6676,11 +6672,12 @@
return false;
}
Objects.requireNonNull(who, "ComponentName is null");
-
// TODO: If an unaffiliated user is removed, the admin will be able to request a bugreport
// which could still contain data related to that user. Should we disallow that, e.g. until
// next boot? Might not be needed given that this still requires user consent.
- ensureDeviceOwnerAndAllUsersAffiliated(who);
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
+ ensureAllUsersAffiliated();
if (mRemoteBugreportServiceIsActive.get()
|| (getDeviceOwnerRemoteBugreportUri() != null)) {
@@ -8489,6 +8486,7 @@
@Override
public void setDefaultSmsApplication(ComponentName admin, String packageName, boolean parent) {
Objects.requireNonNull(admin, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(admin);
if (parent) {
ActiveAdmin ap = getActiveAdminForCallerLocked(admin,
@@ -8497,7 +8495,7 @@
mInjector.binderWithCleanCallingIdentity(() -> enforcePackageIsSystemPackage(
packageName, getProfileParentId(mInjector.userHandleGetCallingUserId())));
} else {
- enforceDeviceOwner(admin);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
}
mInjector.binderWithCleanCallingIdentity(() ->
@@ -9259,14 +9257,14 @@
public boolean removeUser(ComponentName who, UserHandle userHandle) {
Objects.requireNonNull(who, "ComponentName is null");
Objects.requireNonNull(userHandle, "UserHandle is null");
- enforceDeviceOwner(who);
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
- final int callingUserId = mInjector.userHandleGetCallingUserId();
return mInjector.binderWithCleanCallingIdentity(() -> {
String restriction = isManagedProfile(userHandle.getIdentifier())
? UserManager.DISALLOW_REMOVE_MANAGED_PROFILE
: UserManager.DISALLOW_REMOVE_USER;
- if (isAdminAffectedByRestriction(who, restriction, callingUserId)) {
+ if (isAdminAffectedByRestriction(who, restriction, identity.getUserId())) {
Log.w(LOG_TAG, "The device owner cannot remove a user because "
+ restriction + " is enabled, and was not set by the device owner");
return false;
@@ -9292,10 +9290,10 @@
@Override
public boolean switchUser(ComponentName who, UserHandle userHandle) {
Objects.requireNonNull(who, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
synchronized (getLockObject()) {
- getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
-
long id = mInjector.binderClearCallingIdentity();
try {
int userId = UserHandle.USER_SYSTEM;
@@ -9316,7 +9314,8 @@
public int startUserInBackground(ComponentName who, UserHandle userHandle) {
Objects.requireNonNull(who, "ComponentName is null");
Objects.requireNonNull(userHandle, "UserHandle is null");
- enforceDeviceOwner(who);
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
final int userId = userHandle.getIdentifier();
if (isManagedProfile(userId)) {
@@ -9348,7 +9347,8 @@
public int stopUser(ComponentName who, UserHandle userHandle) {
Objects.requireNonNull(who, "ComponentName is null");
Objects.requireNonNull(userHandle, "UserHandle is null");
- enforceDeviceOwner(who);
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
final int userId = userHandle.getIdentifier();
if (isManagedProfile(userId)) {
@@ -9416,7 +9416,8 @@
@Override
public List<UserHandle> getSecondaryUsers(ComponentName who) {
Objects.requireNonNull(who, "ComponentName is null");
- enforceDeviceOwner(who);
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
return mInjector.binderWithCleanCallingIdentity(() -> {
final List<UserInfo> userInfos = mInjector.getUserManager().getUsers(true
@@ -10378,6 +10379,8 @@
@Override
public void setGlobalSetting(ComponentName who, String setting, String value) {
Objects.requireNonNull(who, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_GLOBAL_SETTING)
@@ -10386,8 +10389,6 @@
.write();
synchronized (getLockObject()) {
- getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
-
// Some settings are no supported any more. However we do not want to throw a
// SecurityException to avoid breaking apps.
if (GLOBAL_SETTINGS_DEPRECATED.contains(setting)) {
@@ -10468,7 +10469,7 @@
@Override
public void setLocationEnabled(ComponentName who, boolean locationEnabled) {
- CallerIdentity identity = getCallerIdentity(who);
+ final CallerIdentity identity = getCallerIdentity(who);
Preconditions.checkCallAuthorization(isDeviceOwner(identity));
mInjector.binderWithCleanCallingIdentity(() -> {
@@ -12008,16 +12009,18 @@
@Override
public boolean isSystemOnlyUser(ComponentName admin) {
- enforceDeviceOwner(admin);
- final int callingUserId = mInjector.userHandleGetCallingUserId();
- return UserManager.isSplitSystemUser() && callingUserId == UserHandle.USER_SYSTEM;
+ Objects.requireNonNull(admin, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(admin);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
+ return UserManager.isSplitSystemUser() && identity.getUserId() == UserHandle.USER_SYSTEM;
}
@Override
public void reboot(ComponentName admin) {
- Objects.requireNonNull(admin);
- // Make sure caller has DO.
- enforceDeviceOwner(admin);
+ Objects.requireNonNull(admin, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(admin);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
+
mInjector.binderWithCleanCallingIdentity(() -> {
// Make sure there are no ongoing calls on the device.
if (mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE) {
@@ -13523,18 +13526,18 @@
if (!mHasFeature) {
return;
}
- Objects.requireNonNull(admin);
+ Objects.requireNonNull(admin, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(admin);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
synchronized (getLockObject()) {
- ActiveAdmin deviceOwner =
- getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
-
+ ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
if (deviceOwner.isLogoutEnabled == enabled) {
// already in the requested state
return;
}
deviceOwner.isLogoutEnabled = enabled;
- saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+ saveSettingsLocked(identity.getUserId());
}
}
@@ -13700,20 +13703,20 @@
if (!mHasFeature) {
return;
}
- Objects.requireNonNull(admin);
+ Objects.requireNonNull(admin, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(admin);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
final String startUserSessionMessageString =
startUserSessionMessage != null ? startUserSessionMessage.toString() : null;
synchronized (getLockObject()) {
- final ActiveAdmin deviceOwner =
- getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
-
+ final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
if (TextUtils.equals(deviceOwner.startUserSessionMessage, startUserSessionMessage)) {
return;
}
deviceOwner.startUserSessionMessage = startUserSessionMessageString;
- saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+ saveSettingsLocked(identity.getUserId());
}
mInjector.getActivityManagerInternal()
@@ -13725,20 +13728,20 @@
if (!mHasFeature) {
return;
}
- Objects.requireNonNull(admin);
+ Objects.requireNonNull(admin, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(admin);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
final String endUserSessionMessageString =
endUserSessionMessage != null ? endUserSessionMessage.toString() : null;
synchronized (getLockObject()) {
- final ActiveAdmin deviceOwner =
- getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
-
+ final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
if (TextUtils.equals(deviceOwner.endUserSessionMessage, endUserSessionMessage)) {
return;
}
deviceOwner.endUserSessionMessage = endUserSessionMessageString;
- saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+ saveSettingsLocked(identity.getUserId());
}
mInjector.getActivityManagerInternal()
@@ -13750,11 +13753,12 @@
if (!mHasFeature) {
return null;
}
- Objects.requireNonNull(admin);
+ Objects.requireNonNull(admin, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(admin);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
synchronized (getLockObject()) {
- final ActiveAdmin deviceOwner =
- getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
return deviceOwner.startUserSessionMessage;
}
}
@@ -13764,11 +13768,12 @@
if (!mHasFeature) {
return null;
}
- Objects.requireNonNull(admin);
+ Objects.requireNonNull(admin, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(admin);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
synchronized (getLockObject()) {
- final ActiveAdmin deviceOwner =
- getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
return deviceOwner.endUserSessionMessage;
}
}
@@ -13807,9 +13812,10 @@
if (!mHasFeature || !mHasTelephonyFeature) {
return -1;
}
- Objects.requireNonNull(who, "ComponentName is null in addOverrideApn");
+ Objects.requireNonNull(who, "ComponentName is null");
Objects.requireNonNull(apnSetting, "ApnSetting is null in addOverrideApn");
- enforceDeviceOwner(who);
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
if (tm != null) {
@@ -13827,9 +13833,10 @@
if (!mHasFeature || !mHasTelephonyFeature) {
return false;
}
- Objects.requireNonNull(who, "ComponentName is null in updateOverrideApn");
+ Objects.requireNonNull(who, "ComponentName is null");
Objects.requireNonNull(apnSetting, "ApnSetting is null in updateOverrideApn");
- enforceDeviceOwner(who);
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
if (apnId < 0) {
return false;
@@ -13849,9 +13856,9 @@
if (!mHasFeature || !mHasTelephonyFeature) {
return false;
}
- Objects.requireNonNull(who, "ComponentName is null in removeOverrideApn");
- enforceDeviceOwner(who);
-
+ Objects.requireNonNull(who, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
return removeOverrideApnUnchecked(apnId);
}
@@ -13870,9 +13877,9 @@
if (!mHasFeature || !mHasTelephonyFeature) {
return Collections.emptyList();
}
- Objects.requireNonNull(who, "ComponentName is null in getOverrideApns");
- enforceDeviceOwner(who);
-
+ Objects.requireNonNull(who, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
return getOverrideApnsUnchecked();
}
@@ -13891,9 +13898,9 @@
if (!mHasFeature || !mHasTelephonyFeature) {
return;
}
- Objects.requireNonNull(who, "ComponentName is null in setOverrideApnEnabled");
- enforceDeviceOwner(who);
-
+ Objects.requireNonNull(who, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
setOverrideApnsEnabledUnchecked(enabled);
}
@@ -13909,8 +13916,9 @@
if (!mHasFeature || !mHasTelephonyFeature) {
return false;
}
- Objects.requireNonNull(who, "ComponentName is null in isOverrideApnEnabled");
- enforceDeviceOwner(who);
+ Objects.requireNonNull(who, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
Cursor enforceCursor = mInjector.binderWithCleanCallingIdentity(
() -> mContext.getContentResolver().query(
@@ -13992,11 +14000,9 @@
if (!mHasFeature) {
return PRIVATE_DNS_SET_ERROR_FAILURE_SETTING;
}
-
Objects.requireNonNull(who, "ComponentName is null");
- enforceDeviceOwner(who);
-
- final int returnCode;
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
switch (mode) {
case PRIVATE_DNS_MODE_OPPORTUNISTIC:
@@ -14030,9 +14036,10 @@
if (!mHasFeature) {
return PRIVATE_DNS_MODE_UNKNOWN;
}
-
Objects.requireNonNull(who, "ComponentName is null");
- enforceDeviceOwner(who);
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
+
String currentMode = mInjector.settingsGlobalGetString(PRIVATE_DNS_MODE);
if (currentMode == null) {
currentMode = ConnectivityManager.PRIVATE_DNS_DEFAULT_MODE_FALLBACK;
@@ -14054,10 +14061,9 @@
if (!mHasFeature) {
return null;
}
-
Objects.requireNonNull(who, "ComponentName is null");
- enforceDeviceOwner(who);
-
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
return mInjector.settingsGlobalGetString(PRIVATE_DNS_SPECIFIER);
}
@@ -14402,13 +14408,13 @@
@Override
public void setUserControlDisabledPackages(ComponentName who, List<String> packages) {
- Preconditions.checkNotNull(who, "ComponentName is null");
+ Objects.requireNonNull(who, "ComponentName is null");
Preconditions.checkNotNull(packages, "packages is null");
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
- enforceDeviceOwner(who);
synchronized (getLockObject()) {
- final int userHandle = mInjector.userHandleGetCallingUserId();
- setUserControlDisabledPackagesLocked(userHandle, packages);
+ setUserControlDisabledPackagesLocked(identity.getUserId(), packages);
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_USER_CONTROL_DISABLED_PACKAGES)
.setAdmin(who)
@@ -14428,12 +14434,12 @@
@Override
public List<String> getUserControlDisabledPackages(ComponentName who) {
- Preconditions.checkNotNull(who, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
- enforceDeviceOwner(who);
- final int userHandle = mInjector.binderGetCallingUserHandle().getIdentifier();
synchronized (getLockObject()) {
- final List<String> packages = getUserData(userHandle).mUserControlDisabledPackages;
+ final List<String> packages =
+ getUserData(identity.getUserId()).mUserControlDisabledPackages;
return packages == null ? Collections.EMPTY_LIST : packages;
}
}
diff --git a/services/tests/PackageManagerServiceTests/host/Android.bp b/services/tests/PackageManagerServiceTests/host/Android.bp
index e4e7e22..4f636ef 100644
--- a/services/tests/PackageManagerServiceTests/host/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/Android.bp
@@ -22,6 +22,7 @@
],
static_libs: [
"frameworks-base-hostutils",
+ "PackageManagerServiceHostTestsIntentVerifyUtils",
],
test_suites: ["general-tests"],
java_resources: [
@@ -33,7 +34,15 @@
":PackageManagerTestAppVersion4",
":PackageManagerTestAppOriginalOverride",
":PackageManagerServiceDeviceSideTests",
- ],
+ ":PackageManagerTestIntentVerifier",
+ ":PackageManagerTestIntentVerifierTarget1",
+ ":PackageManagerTestIntentVerifierTarget2",
+ ":PackageManagerTestIntentVerifierTarget3",
+ ":PackageManagerTestIntentVerifierTarget4Base",
+ ":PackageManagerTestIntentVerifierTarget4NoAutoVerify",
+ ":PackageManagerTestIntentVerifierTarget4Wildcard",
+ ":PackageManagerTestIntentVerifierTarget4WildcardNoAutoVerify",
+ ]
}
genrule {
diff --git a/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/Android.bp b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/Android.bp
new file mode 100644
index 0000000..b7a0624
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/Android.bp
@@ -0,0 +1,19 @@
+// Copyright (C) 2020 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.
+
+java_library {
+ name: "PackageManagerServiceHostTestsIntentVerifyUtils",
+ srcs: ["src/**/*.kt"],
+ host_supported: true,
+}
diff --git a/tests/AutoVerify/app1/src/com/android/test/autoverify/MainActivity.java b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/src/com/android/server/pm/test/intent/verify/IntentVerifyTestParams.kt
similarity index 78%
rename from tests/AutoVerify/app1/src/com/android/test/autoverify/MainActivity.java
rename to services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/src/com/android/server/pm/test/intent/verify/IntentVerifyTestParams.kt
index 09ef472..48119e0 100644
--- a/tests/AutoVerify/app1/src/com/android/test/autoverify/MainActivity.java
+++ b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/src/com/android/server/pm/test/intent/verify/IntentVerifyTestParams.kt
@@ -13,3 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
+package com.android.server.pm.test.intent.verify
+
+interface IntentVerifyTestParams {
+
+ val methodName: String
+
+ fun toArgsMap(): Map<String, String>
+}
diff --git a/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/src/com/android/server/pm/test/intent/verify/SetActivityAsAlwaysParams.kt b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/src/com/android/server/pm/test/intent/verify/SetActivityAsAlwaysParams.kt
new file mode 100644
index 0000000..26c3903
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/src/com/android/server/pm/test/intent/verify/SetActivityAsAlwaysParams.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 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.pm.test.intent.verify
+
+data class SetActivityAsAlwaysParams(
+ val uri: String,
+ val packageName: String,
+ val activityName: String,
+ override val methodName: String = "setActivityAsAlways"
+) : IntentVerifyTestParams {
+
+ companion object {
+ private const val KEY_URI = "uri"
+ private const val KEY_PACKAGE_NAME = "packageName"
+ private const val KEY_ACTIVITY_NAME = "activityName"
+
+ fun fromArgs(args: Map<String, String>) = SetActivityAsAlwaysParams(
+ args.getValue(KEY_URI),
+ args.getValue(KEY_PACKAGE_NAME),
+ args.getValue(KEY_ACTIVITY_NAME)
+ )
+ }
+
+ override fun toArgsMap() = mapOf(
+ KEY_URI to uri,
+ KEY_PACKAGE_NAME to packageName,
+ KEY_ACTIVITY_NAME to activityName
+ )
+}
diff --git a/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/src/com/android/server/pm/test/intent/verify/StartActivityParams.kt b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/src/com/android/server/pm/test/intent/verify/StartActivityParams.kt
new file mode 100644
index 0000000..7eddcfb
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/src/com/android/server/pm/test/intent/verify/StartActivityParams.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020 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.pm.test.intent.verify
+
+data class StartActivityParams(
+ val uri: String,
+ val expected: List<String>,
+ val withBrowsers: Boolean = false,
+ override val methodName: String = "verifyActivityStart"
+) : IntentVerifyTestParams {
+ companion object {
+ private const val KEY_URI = "uri"
+ private const val KEY_EXPECTED = "expected"
+ private const val KEY_BROWSER = "browser"
+
+ fun fromArgs(args: Map<String, String>) = StartActivityParams(
+ args.getValue(KEY_URI),
+ args.getValue(KEY_EXPECTED).split(","),
+ args.getValue(KEY_BROWSER).toBoolean()
+ )
+ }
+
+ constructor(
+ uri: String,
+ expected: String,
+ withBrowsers: Boolean = false
+ ) : this(uri, listOf(expected), withBrowsers)
+
+ override fun toArgsMap() = mapOf(
+ KEY_URI to uri,
+ KEY_EXPECTED to expected.joinToString(separator = ","),
+ KEY_BROWSER to withBrowsers.toString()
+ )
+}
diff --git a/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/src/com/android/server/pm/test/intent/verify/VerifyRequest.kt b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/src/com/android/server/pm/test/intent/verify/VerifyRequest.kt
new file mode 100644
index 0000000..f93b1e0
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/src/com/android/server/pm/test/intent/verify/VerifyRequest.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020 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.pm.test.intent.verify
+
+data class VerifyRequest(
+ val id: Int = -1,
+ val scheme: String,
+ val hosts: List<String>,
+ val packageName: String
+) {
+
+ companion object {
+ fun deserialize(value: String?): VerifyRequest {
+ val lines = value?.trim()?.lines()
+ ?: return VerifyRequest(scheme = "", hosts = emptyList(), packageName = "")
+ return VerifyRequest(
+ lines[0].removePrefix("id=").toInt(),
+ lines[1].removePrefix("scheme="),
+ lines[2].removePrefix("hosts=").split(","),
+ lines[3].removePrefix("packageName=")
+ )
+ }
+ }
+
+ constructor(id: Int = -1, scheme: String, host: String, packageName: String) :
+ this(id, scheme, listOf(host), packageName)
+
+ fun serializeToString() = """
+ id=$id
+ scheme=$scheme
+ hosts=${hosts.joinToString(separator = ",")}
+ packageName=$packageName
+ """.trimIndent() + "\n"
+}
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/FactoryPackageTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/FactoryPackageTest.kt
index 3847658..e17358d 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/FactoryPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/FactoryPackageTest.kt
@@ -31,7 +31,8 @@
private val preparer: SystemPreparer = SystemPreparer(tempFolder,
SystemPreparer.RebootStrategy.FULL, deviceRebootRule) { this.device }
- @get:Rule
+ @Rule
+ @JvmField
val rules = RuleChain.outerRule(tempFolder).around(preparer)!!
private val filePath =
HostUtils.makePathForApk("PackageManagerTestApp.apk", Partition.SYSTEM)
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt
index 8dfefaf..24c714c 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt
@@ -18,6 +18,7 @@
import com.android.internal.util.test.SystemPreparer
import com.android.tradefed.device.ITestDevice
+import org.junit.rules.TemporaryFolder
import java.io.File
import java.io.FileOutputStream
@@ -34,6 +35,19 @@
}
}
+internal fun ITestDevice.installJavaResourceApk(
+ tempFolder: TemporaryFolder,
+ javaResource: String,
+ reinstall: Boolean = true,
+ extraArgs: Array<String> = emptyArray()
+): String? {
+ val file = HostUtils.copyResourceToHostFile(javaResource, tempFolder.newFile())
+ return installPackage(file, reinstall, *extraArgs)
+}
+
+internal fun ITestDevice.uninstallPackages(vararg pkgNames: String) =
+ pkgNames.forEach { uninstallPackage(it) }
+
internal object HostUtils {
fun getDataDir(device: ITestDevice, pkgName: String) =
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt
index b7d1359..37c999c 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt
@@ -47,7 +47,8 @@
private val preparer: SystemPreparer = SystemPreparer(tempFolder,
SystemPreparer.RebootStrategy.FULL, deviceRebootRule) { this.device }
- @get:Rule
+ @Rule
+ @JvmField
val rules = RuleChain.outerRule(tempFolder).around(preparer)!!
private val filePath = HostUtils.makePathForApk("PackageManagerTestApp.apk", Partition.PRODUCT)
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt
index 4ae3ca5..4becae6 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt
@@ -47,7 +47,8 @@
private val preparer: SystemPreparer = SystemPreparer(tempFolder,
SystemPreparer.RebootStrategy.FULL, deviceRebootRule) { this.device }
- @get:Rule
+ @Rule
+ @JvmField
val rules = RuleChain.outerRule(tempFolder).around(preparer)!!
@Before
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/Partition.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/Partition.kt
index 654c11c..6479f58 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/Partition.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/Partition.kt
@@ -22,6 +22,7 @@
// Unfortunately no easy way to access PMS SystemPartitions, so mock them here
internal enum class Partition(val baseAppFolder: Path) {
SYSTEM("/system/app"),
+ SYSTEM_PRIVILEGED("/system/priv-app"),
VENDOR("/vendor/app"),
PRODUCT("/product/app"),
SYSTEM_EXT("/system_ext/app")
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt
index 207f10a..46120af 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt
@@ -110,7 +110,8 @@
private val preparer: SystemPreparer = SystemPreparer(tempFolder,
SystemPreparer.RebootStrategy.FULL, deviceRebootRule) { this.device }
- @get:Rule
+ @Rule
+ @JvmField
val rules = RuleChain.outerRule(tempFolder).let {
if (DEBUG_NO_REBOOT) {
it!!
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/intent/verify/IntentFilterVerificationTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/intent/verify/IntentFilterVerificationTest.kt
new file mode 100644
index 0000000..fffda8e
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/intent/verify/IntentFilterVerificationTest.kt
@@ -0,0 +1,413 @@
+/*
+ * Copyright (C) 2020 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.pm.test.intent.verify
+
+import com.android.internal.util.test.SystemPreparer
+import com.android.server.pm.test.Partition
+import com.android.server.pm.test.deleteApkFolders
+import com.android.server.pm.test.installJavaResourceApk
+import com.android.server.pm.test.pushApk
+import com.android.server.pm.test.uninstallPackages
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.ClassRule
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.RuleChain
+import org.junit.rules.TemporaryFolder
+import org.junit.runner.RunWith
+import java.io.File
+import java.util.concurrent.TimeUnit
+
+@RunWith(DeviceJUnit4ClassRunner::class)
+class IntentFilterVerificationTest : BaseHostJUnit4Test() {
+
+ companion object {
+ private const val VERIFIER = "PackageManagerTestIntentVerifier.apk"
+ private const val VERIFIER_PKG_NAME = "com.android.server.pm.test.intent.verifier"
+ private const val TARGET_PKG_PREFIX = "$VERIFIER_PKG_NAME.target"
+ private const val TARGET_APK_PREFIX = "PackageManagerTestIntentVerifierTarget"
+ private const val TARGET_ONE = "${TARGET_APK_PREFIX}1.apk"
+ private const val TARGET_ONE_PKG_NAME = "$TARGET_PKG_PREFIX.one"
+ private const val TARGET_TWO = "${TARGET_APK_PREFIX}2.apk"
+ private const val TARGET_TWO_PKG_NAME = "$TARGET_PKG_PREFIX.two"
+ private const val TARGET_THREE = "${TARGET_APK_PREFIX}3.apk"
+ private const val TARGET_THREE_PKG_NAME = "$TARGET_PKG_PREFIX.three"
+ private const val TARGET_FOUR_BASE = "${TARGET_APK_PREFIX}4Base.apk"
+ private const val TARGET_FOUR_PKG_NAME = "$TARGET_PKG_PREFIX.four"
+ private const val TARGET_FOUR_NO_AUTO_VERIFY = "${TARGET_APK_PREFIX}4NoAutoVerify.apk"
+ private const val TARGET_FOUR_WILDCARD = "${TARGET_APK_PREFIX}4Wildcard.apk"
+ private const val TARGET_FOUR_WILDCARD_NO_AUTO_VERIFY =
+ "${TARGET_APK_PREFIX}4WildcardNoAutoVerify.apk"
+
+ @get:ClassRule
+ val deviceRebootRule = SystemPreparer.TestRuleDelegate(true)
+ }
+
+ private val tempFolder = TemporaryFolder()
+ private val preparer: SystemPreparer = SystemPreparer(tempFolder,
+ SystemPreparer.RebootStrategy.FULL, deviceRebootRule) { this.device }
+
+ @Rule
+ @JvmField
+ val rules = RuleChain.outerRule(tempFolder).around(preparer)!!
+
+ private val permissionsFile = File("/system/etc/permissions" +
+ "/privapp-PackageManagerIntentFilterVerificationTest-permissions.xml")
+
+ @Before
+ fun cleanupAndPushPermissionsFile() {
+ // In order for the test app to be the verification agent, it needs a permission file
+ // which can be pushed onto the system and removed afterwards.
+ val file = tempFolder.newFile().apply {
+ """
+ <permissions>
+ <privapp-permissions package="$VERIFIER_PKG_NAME">
+ <permission name="android.permission.INTENT_FILTER_VERIFICATION_AGENT"/>
+ </privapp-permissions>
+ </permissions>
+ """
+ .trimIndent()
+ .let { writeText(it) }
+ }
+ device.uninstallPackages(TARGET_ONE_PKG_NAME, TARGET_TWO_PKG_NAME, TARGET_THREE_PKG_NAME,
+ TARGET_FOUR_PKG_NAME)
+ preparer.pushApk(VERIFIER, Partition.SYSTEM_PRIVILEGED)
+ .pushFile(file, permissionsFile.toString())
+ .reboot()
+ runTest("clearResponse")
+ }
+
+ @After
+ fun cleanupAndDeletePermissionsFile() {
+ device.uninstallPackages(TARGET_ONE_PKG_NAME, TARGET_TWO_PKG_NAME, TARGET_THREE_PKG_NAME,
+ TARGET_FOUR_PKG_NAME)
+ preparer.deleteApkFolders(Partition.SYSTEM_PRIVILEGED, VERIFIER)
+ .deleteFile(permissionsFile.toString())
+ device.reboot()
+ }
+
+ @Test
+ fun verifyOne() {
+ installPackage(TARGET_ONE)
+
+ assertReceivedRequests(true, VerifyRequest(
+ scheme = "https",
+ hosts = listOf(
+ "https_only.pm.server.android.com",
+ "other_activity.pm.server.android.com",
+ "http_only.pm.server.android.com",
+ "verify.pm.server.android.com",
+ "https_plus_non_web_scheme.pm.server.android.com",
+ "multiple.pm.server.android.com",
+ // TODO(b/159952358): the following domain should not be
+ // verified, this is because the verifier tries to verify all web domains,
+ // even in intent filters not marked for auto verify
+ "no_verify.pm.server.android.com"
+ ),
+ packageName = TARGET_ONE_PKG_NAME
+ ))
+
+ runTest(StartActivityParams(
+ uri = "https://https_only.pm.server.android.com",
+ expected = "$TARGET_ONE_PKG_NAME.TargetActivity"
+ ))
+ }
+
+ @Test
+ fun nonWebScheme() {
+ installPackage(TARGET_TWO)
+ assertReceivedRequests(null)
+ }
+
+ @Test
+ fun verifyHttpNonSecureOnly() {
+ installPackage(TARGET_THREE)
+ assertReceivedRequests(true, VerifyRequest(
+ scheme = "https",
+ hosts = listOf(
+ "multiple.pm.server.android.com"
+ ),
+ packageName = TARGET_THREE_PKG_NAME
+ ))
+
+ runTest(StartActivityParams(
+ uri = "http://multiple.pm.server.android.com",
+ expected = "$TARGET_THREE_PKG_NAME.TargetActivity"
+ ))
+ }
+
+ @Test
+ fun multipleResults() {
+ installPackage(TARGET_ONE)
+ installPackage(TARGET_THREE)
+ assertReceivedRequests(true, VerifyRequest(
+ scheme = "https",
+ hosts = listOf(
+ "https_only.pm.server.android.com",
+ "other_activity.pm.server.android.com",
+ "http_only.pm.server.android.com",
+ "verify.pm.server.android.com",
+ "https_plus_non_web_scheme.pm.server.android.com",
+ "multiple.pm.server.android.com",
+ // TODO(b/159952358): the following domain should not be
+ // verified, this is because the verifier tries to verify all web domains,
+ // even in intent filters not marked for auto verify
+ "no_verify.pm.server.android.com"
+ ),
+ packageName = TARGET_ONE_PKG_NAME
+ ), VerifyRequest(
+ scheme = "https",
+ hosts = listOf(
+ "multiple.pm.server.android.com"
+ ),
+ packageName = TARGET_THREE_PKG_NAME
+ ))
+
+ // Target3 declares http non-s, so it should be included in the set here
+ runTest(StartActivityParams(
+ uri = "http://multiple.pm.server.android.com",
+ expected = listOf(
+ "$TARGET_ONE_PKG_NAME.TargetActivity2",
+ "$TARGET_THREE_PKG_NAME.TargetActivity"
+ )
+ ))
+
+ // But it excludes https, so it shouldn't resolve here
+ runTest(StartActivityParams(
+ uri = "https://multiple.pm.server.android.com",
+ expected = "$TARGET_ONE_PKG_NAME.TargetActivity2"
+ ))
+
+ // Remove Target3 and return to single verified Target1 app for http non-s
+ device.uninstallPackage(TARGET_THREE_PKG_NAME)
+ runTest(StartActivityParams(
+ uri = "http://multiple.pm.server.android.com",
+ expected = "$TARGET_ONE_PKG_NAME.TargetActivity2"
+ ))
+ }
+
+ @Test
+ fun demoteAlways() {
+ installPackage(TARGET_FOUR_BASE)
+ assertReceivedRequests(false, VerifyRequest(
+ scheme = "https",
+ host = "failing.pm.server.android.com",
+ packageName = TARGET_FOUR_PKG_NAME
+ ))
+
+ runTest(StartActivityParams(
+ uri = "https://failing.pm.server.android.com",
+ expected = "$TARGET_FOUR_PKG_NAME.TargetActivity",
+ withBrowsers = true
+ ))
+ runTest(SetActivityAsAlwaysParams(
+ uri = "https://failing.pm.server.android.com",
+ packageName = TARGET_FOUR_PKG_NAME,
+ activityName = "$TARGET_FOUR_PKG_NAME.TargetActivity"
+ ))
+ runTest(StartActivityParams(
+ uri = "https://failing.pm.server.android.com",
+ expected = "$TARGET_FOUR_PKG_NAME.TargetActivity"
+ ))
+
+ // Re-installing with same host/verify set will maintain always setting
+ installPackage(TARGET_FOUR_BASE)
+ assertReceivedRequests(null)
+ runTest(StartActivityParams(
+ uri = "https://failing.pm.server.android.com",
+ expected = "$TARGET_FOUR_PKG_NAME.TargetActivity"
+ ))
+
+ // Installing with new wildcard host will downgrade out of always, re-including browsers
+ installPackage(TARGET_FOUR_WILDCARD)
+
+ // TODO(b/159952358): The first request without the wildcard should not be sent. This is
+ // caused by the request being queued even if it should be dropped from the previous
+ // install case since the host set didn't change.
+ assertReceivedRequests(false, VerifyRequest(
+ scheme = "https",
+ hosts = listOf("failing.pm.server.android.com"),
+ packageName = TARGET_FOUR_PKG_NAME
+ ), VerifyRequest(
+ scheme = "https",
+ hosts = listOf("failing.pm.server.android.com", "wildcard.tld"),
+ packageName = TARGET_FOUR_PKG_NAME
+ ))
+ runTest(StartActivityParams(
+ uri = "https://failing.pm.server.android.com",
+ expected = "$TARGET_FOUR_PKG_NAME.TargetActivity",
+ withBrowsers = true
+ ))
+ }
+
+ @Test
+ fun unverifiedReinstallResendRequest() {
+ installPackage(TARGET_FOUR_BASE)
+ assertReceivedRequests(false, VerifyRequest(
+ scheme = "https",
+ host = "failing.pm.server.android.com",
+ packageName = TARGET_FOUR_PKG_NAME
+ ))
+
+ installPackage(TARGET_FOUR_BASE)
+
+ assertReceivedRequests(false, VerifyRequest(
+ scheme = "https",
+ host = "failing.pm.server.android.com",
+ packageName = TARGET_FOUR_PKG_NAME
+ ))
+ }
+
+ @Test
+ fun unverifiedUpdateRemovingDomainNoRequestDemoteAlways() {
+ installPackage(TARGET_FOUR_WILDCARD)
+ assertReceivedRequests(false, VerifyRequest(
+ scheme = "https",
+ hosts = listOf("failing.pm.server.android.com", "wildcard.tld"),
+ packageName = TARGET_FOUR_PKG_NAME
+ ))
+
+ runTest(SetActivityAsAlwaysParams(
+ uri = "https://failing.pm.server.android.com",
+ packageName = TARGET_FOUR_PKG_NAME,
+ activityName = "$TARGET_FOUR_PKG_NAME.TargetActivity"
+ ))
+
+ // Re-installing with a smaller host/verify set will not request re-verification
+ installPackage(TARGET_FOUR_BASE)
+ assertReceivedRequests(null)
+ runTest(StartActivityParams(
+ uri = "https://failing.pm.server.android.com",
+ expected = "$TARGET_FOUR_PKG_NAME.TargetActivity"
+ ))
+
+ // Re-installing with a (now) larger host/verify set will re-request and demote
+ installPackage(TARGET_FOUR_WILDCARD)
+ // TODO(b/159952358): The first request should not be sent. This is caused by the request
+ // being queued even if it should be dropped from the previous install case.
+ assertReceivedRequests(false, VerifyRequest(
+ scheme = "https",
+ host = "failing.pm.server.android.com",
+ packageName = TARGET_FOUR_PKG_NAME
+ ), VerifyRequest(
+ scheme = "https",
+ hosts = listOf("failing.pm.server.android.com", "wildcard.tld"),
+ packageName = TARGET_FOUR_PKG_NAME
+ ))
+
+ runTest(StartActivityParams(
+ uri = "https://failing.pm.server.android.com",
+ expected = "$TARGET_FOUR_PKG_NAME.TargetActivity",
+ withBrowsers = true
+ ))
+ }
+
+ // TODO(b/159952358): I would expect this to demote
+ // TODO(b/32810168)
+ @Test
+ fun verifiedUpdateRemovingAutoVerifyMaintainsAlways() {
+ installPackage(TARGET_FOUR_BASE)
+ assertReceivedRequests(true, VerifyRequest(
+ scheme = "https",
+ host = "failing.pm.server.android.com",
+ packageName = TARGET_FOUR_PKG_NAME
+ ))
+
+ runTest(StartActivityParams(
+ uri = "https://failing.pm.server.android.com",
+ expected = "$TARGET_FOUR_PKG_NAME.TargetActivity"
+ ))
+
+ installPackage(TARGET_FOUR_NO_AUTO_VERIFY)
+ assertReceivedRequests(null)
+
+ runTest(StartActivityParams(
+ uri = "https://failing.pm.server.android.com",
+ expected = "$TARGET_FOUR_PKG_NAME.TargetActivity"
+ ))
+ }
+
+ @Test
+ fun verifiedUpdateRemovingAutoVerifyAddingDomainDemotesAlways() {
+ installPackage(TARGET_FOUR_BASE)
+
+ assertReceivedRequests(true, VerifyRequest(
+ scheme = "https",
+ host = "failing.pm.server.android.com",
+ packageName = TARGET_FOUR_PKG_NAME
+ ))
+
+ runTest(StartActivityParams(
+ uri = "https://failing.pm.server.android.com",
+ expected = "$TARGET_FOUR_PKG_NAME.TargetActivity"
+ ))
+
+ installPackage(TARGET_FOUR_WILDCARD_NO_AUTO_VERIFY)
+ assertReceivedRequests(null)
+
+ runTest(StartActivityParams(
+ uri = "https://failing.pm.server.android.com",
+ expected = "$TARGET_FOUR_PKG_NAME.TargetActivity",
+ withBrowsers = true
+ ))
+ }
+
+ private fun installPackage(javaResourceName: String) {
+ // Need to pass --user as verification is not currently run for all user installs
+ assertThat(device.installJavaResourceApk(tempFolder, javaResourceName,
+ extraArgs = arrayOf("--user", device.currentUser.toString()))).isNull()
+ }
+
+ private fun assertReceivedRequests(success: Boolean?, vararg expected: VerifyRequest?) {
+ // TODO(b/159952358): This can probably be less than 10
+ // Because tests have to assert that multiple broadcasts aren't received, there's no real
+ // better way to await for a value than sleeping for a long enough time.
+ TimeUnit.SECONDS.sleep(10)
+
+ val params = mutableMapOf<String, String>()
+ if (expected.any { it != null }) {
+ params["expected"] = expected.filterNotNull()
+ .joinToString(separator = "") { it.serializeToString() }
+ }
+ runTest("compareLastReceived", params)
+
+ if (success != null) {
+ if (success) {
+ runTest("verifyPreviousReceivedSuccess")
+ } else {
+ runTest("verifyPreviousReceivedFailure")
+ }
+ runTest("clearResponse")
+ }
+ }
+
+ private fun runTest(params: IntentVerifyTestParams) =
+ runTest(params.methodName, params.toArgsMap())
+
+ private fun runTest(testName: String, args: Map<String, String> = emptyMap()) {
+ val escapedArgs = args.mapValues {
+ // Need to escape strings so that args are passed properly through the shell command
+ "\"${it.value.trim('"')}\""
+ }
+ runDeviceTests(device, null, VERIFIER_PKG_NAME, "$VERIFIER_PKG_NAME.VerifyReceiverTest",
+ testName, null, 10 * 60 * 1000L, 10 * 60 * 1000L, 0L, true, false, escapedArgs)
+ }
+}
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/Android.bp
new file mode 100644
index 0000000..e82f57d
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/Android.bp
@@ -0,0 +1,28 @@
+// Copyright (C) 2020 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.
+
+android_test_helper_app {
+ name: "PackageManagerTestIntentVerifier",
+ srcs: [ "src/**/*.kt" ],
+ static_libs: [
+ "androidx.test.core",
+ "androidx.test.espresso.core",
+ "androidx.test.runner",
+ "compatibility-device-util-axt",
+ "junit",
+ "truth-prebuilt",
+ "PackageManagerServiceHostTestsIntentVerifyUtils",
+ ],
+ platform_apis: true,
+}
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/AndroidManifest.xml b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/AndroidManifest.xml
new file mode 100644
index 0000000..17b50b0
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/AndroidManifest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2020 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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.pm.test.intent.verifier"
+ >
+
+ <uses-permission android:name="android.permission.INTENT_FILTER_VERIFICATION_AGENT" />
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+ <uses-permission android:name="android.permission.SET_PREFERRED_APPLICATIONS" />
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.pm.test.intent.verifier"
+ />
+
+ <application>
+ <receiver android:name=".VerifyReceiver" android:exported="true">
+ <intent-filter android:priority="999">
+ <action android:name="android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION"/>
+ <data android:mimeType="application/vnd.android.package-archive"/>
+ </intent-filter>
+ </receiver>
+ </application>
+
+</manifest>
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/src/com/android/server/pm/test/intent/verifier/VerifyReceiver.kt b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/src/com/android/server/pm/test/intent/verifier/VerifyReceiver.kt
new file mode 100644
index 0000000..073c2be
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/src/com/android/server/pm/test/intent/verifier/VerifyReceiver.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 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.pm.test.intent.verifier
+
+import android.content.BroadcastReceiver
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import com.android.server.pm.test.intent.verify.VerifyRequest
+
+class VerifyReceiver : BroadcastReceiver() {
+
+ override fun onReceive(context: Context, intent: Intent) {
+ if (intent.action != Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION) return
+ val params = intent.toVerifyParams()
+
+ // If the receiver is called for a normal request, proxy it to the real verifier on device
+ if (params.hosts.none { it.contains("pm.server.android.com") }) {
+ sendToRealVerifier(context, Intent(intent))
+ return
+ }
+
+ // When the receiver is invoked for a test install, there is no direct connection to host,
+ // so store the result in a file to read and assert on later. Append is intentional so that
+ // amount of invocations and clean up can be verified.
+ context.filesDir.resolve("test.txt")
+ .appendText(params.serializeToString())
+ }
+
+ private fun sendToRealVerifier(context: Context, intent: Intent) {
+ context.packageManager.queryBroadcastReceivers(intent, 0)
+ .first { it.activityInfo?.packageName != context.packageName }
+ .let { it.activityInfo!! }
+ .let { intent.setComponent(ComponentName(it.packageName, it.name)) }
+ .run { context.sendBroadcast(intent) }
+ }
+
+ private fun Intent.toVerifyParams() = VerifyRequest(
+ id = getIntExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_ID, -1),
+ scheme = getStringExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_URI_SCHEME)!!,
+ hosts = getStringExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_HOSTS)!!
+ .split(' '),
+ packageName = getStringExtra(
+ PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_PACKAGE_NAME)!!
+
+ )
+}
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/src/com/android/server/pm/test/intent/verifier/VerifyReceiverTest.kt b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/src/com/android/server/pm/test/intent/verifier/VerifyReceiverTest.kt
new file mode 100644
index 0000000..6de3d4e
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/src/com/android/server/pm/test/intent/verifier/VerifyReceiverTest.kt
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2020 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.pm.test.intent.verifier
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.content.pm.PackageManager
+import android.net.Uri
+import android.os.Bundle
+import android.os.UserHandle
+import androidx.test.InstrumentationRegistry
+import androidx.test.runner.AndroidJUnit4
+import com.android.compatibility.common.util.ShellIdentityUtils
+import com.android.server.pm.test.intent.verify.SetActivityAsAlwaysParams
+import com.android.server.pm.test.intent.verify.StartActivityParams
+import com.android.server.pm.test.intent.verify.VerifyRequest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.io.File
+
+@RunWith(AndroidJUnit4::class)
+class VerifyReceiverTest {
+
+ val args: Bundle = InstrumentationRegistry.getArguments()
+ val context: Context = InstrumentationRegistry.getContext()
+
+ private val file = context.filesDir.resolve("test.txt")
+
+ @Test
+ fun clearResponse() {
+ file.delete()
+ }
+
+ @Test
+ fun compareLastReceived() {
+ val lastReceivedText = file.readTextIfExists()
+ val expectedText = args.getString("expected")
+ if (expectedText.isNullOrEmpty()) {
+ assertThat(lastReceivedText).isEmpty()
+ return
+ }
+
+ val expectedParams = expectedText.parseParams()
+ val lastReceivedParams = lastReceivedText.parseParams()
+
+ assertThat(lastReceivedParams).hasSize(expectedParams.size)
+
+ lastReceivedParams.zip(expectedParams).forEach { (actual, expected) ->
+ assertThat(actual.hosts).containsExactlyElementsIn(expected.hosts)
+ assertThat(actual.packageName).isEqualTo(expected.packageName)
+ assertThat(actual.scheme).isEqualTo(expected.scheme)
+ }
+ }
+
+ @Test
+ fun setActivityAsAlways() {
+ val params = SetActivityAsAlwaysParams.fromArgs(
+ args.keySet().associateWith { args.getString(it)!! })
+ val uri = Uri.parse(params.uri)
+ val filter = IntentFilter().apply {
+ addAction(Intent.ACTION_VIEW)
+ addCategory(Intent.CATEGORY_DEFAULT)
+ addDataScheme(uri.scheme)
+ addDataAuthority(uri.authority, null)
+ }
+
+ val intent = Intent(Intent.ACTION_VIEW, uri).apply {
+ addCategory(Intent.CATEGORY_DEFAULT)
+ }
+ val allResults = context.packageManager.queryIntentActivities(intent, 0)
+ val allComponents = allResults
+ .map { ComponentName(it.activityInfo.packageName, it.activityInfo.name) }
+ .toTypedArray()
+ val matchingInfo = allResults.first {
+ it.activityInfo.packageName == params.packageName &&
+ it.activityInfo.name == params.activityName
+ }
+
+ ShellIdentityUtils.invokeMethodWithShellPermissions(context.packageManager,
+ ShellIdentityUtils.ShellPermissionMethodHelper<Unit, PackageManager> {
+ it.addUniquePreferredActivity(filter, matchingInfo.match, allComponents,
+ ComponentName(matchingInfo.activityInfo.packageName,
+ matchingInfo.activityInfo.name))
+ it.updateIntentVerificationStatusAsUser(params.packageName,
+ PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS,
+ UserHandle.myUserId())
+ }, "android.permission.SET_PREFERRED_APPLICATIONS")
+ }
+
+ @Test
+ fun verifyPreviousReceivedSuccess() {
+ file.readTextIfExists()
+ .parseParams()
+ .forEach {
+ context.packageManager.verifyIntentFilter(it.id,
+ PackageManager.INTENT_FILTER_VERIFICATION_SUCCESS, emptyList())
+ }
+ }
+
+ @Test
+ fun verifyPreviousReceivedFailure() {
+ file.readTextIfExists()
+ .parseParams()
+ .forEach {
+ context.packageManager.verifyIntentFilter(it.id,
+ PackageManager.INTENT_FILTER_VERIFICATION_FAILURE, it.hosts)
+ }
+ }
+
+ @Test
+ fun verifyActivityStart() {
+ val params = StartActivityParams
+ .fromArgs(args.keySet().associateWith { args.getString(it)!! })
+ val uri = Uri.parse(params.uri)
+ val intent = Intent(Intent.ACTION_VIEW).apply {
+ data = uri
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ }
+
+ val expectedActivities = params.expected.toMutableList()
+
+ if (params.withBrowsers) {
+ // Since the host doesn't know what browsers the device has, query here and add it to
+ // set if it's expected that browser are returned
+ val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse("https://example.com"))
+ expectedActivities += context.packageManager.queryIntentActivities(browserIntent, 0)
+ .map { it.activityInfo.name }
+ }
+
+ val infos = context.packageManager.queryIntentActivities(intent, 0)
+ .map { it.activityInfo.name }
+ assertThat(infos).containsExactlyElementsIn(expectedActivities)
+ }
+
+ private fun File.readTextIfExists() = if (exists()) readText() else ""
+
+ // Rudimentary list deserialization by splitting text block into 4 line sections
+ private fun String.parseParams() = trim()
+ .lines()
+ .windowed(4, 4)
+ .map { it.joinToString(separator = "\n") }
+ .map { VerifyRequest.deserialize(it) }
+}
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/Android.bp
new file mode 100644
index 0000000..7161fdd
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/Android.bp
@@ -0,0 +1,48 @@
+// Copyright (C) 2020 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.
+
+android_test_helper_app {
+ name: "PackageManagerTestIntentVerifierTarget1",
+ manifest: "AndroidManifest1.xml",
+}
+
+android_test_helper_app {
+ name: "PackageManagerTestIntentVerifierTarget2",
+ manifest: "AndroidManifest2.xml",
+}
+
+android_test_helper_app {
+ name: "PackageManagerTestIntentVerifierTarget3",
+ manifest: "AndroidManifest3.xml",
+}
+
+android_test_helper_app {
+ name: "PackageManagerTestIntentVerifierTarget4Base",
+ manifest: "AndroidManifest4Base.xml",
+}
+
+android_test_helper_app {
+ name: "PackageManagerTestIntentVerifierTarget4NoAutoVerify",
+ manifest: "AndroidManifest4NoAutoVerify.xml",
+}
+
+android_test_helper_app {
+ name: "PackageManagerTestIntentVerifierTarget4Wildcard",
+ manifest: "AndroidManifest4Wildcard.xml",
+}
+
+android_test_helper_app {
+ name: "PackageManagerTestIntentVerifierTarget4WildcardNoAutoVerify",
+ manifest: "AndroidManifest4WildcardNoAutoVerify.xml",
+}
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest1.xml b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest1.xml
new file mode 100644
index 0000000..6cf5c76
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest1.xml
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2020 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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.pm.test.intent.verifier.target.one" android:versionCode="1">
+
+ <application>
+ <activity android:name=".TargetActivity" android:exported="true">
+ <intent-filter android:autoVerify="true">
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="http" />
+ <data android:scheme="https" />
+ <data android:host="verify.pm.server.android.com" />
+ </intent-filter>
+
+ <intent-filter android:autoVerify="false">
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="http" />
+ <data android:scheme="https" />
+ <data android:host="no_verify.pm.server.android.com" />
+ </intent-filter>
+
+ <intent-filter android:autoVerify="true">
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="http" />
+ <data android:host="http_only.pm.server.android.com" />
+ </intent-filter>
+
+ <intent-filter android:autoVerify="true">
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="https" />
+ <data android:host="https_only.pm.server.android.com" />
+ </intent-filter>
+
+ <intent-filter android:autoVerify="true">
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="htttps" />
+ <data android:host="non_http.pm.server.android.com" />
+ </intent-filter>
+
+ <intent-filter android:autoVerify="true">
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="https" />
+ <data android:scheme="non_web_scheme" />
+ <data android:host="https_plus_non_web_scheme.pm.server.android.com" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name=".TargetActivity2" android:exported="true">
+ <intent-filter android:autoVerify="true">
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="http" />
+ <data android:scheme="https" />
+ <data android:host="other_activity.pm.server.android.com" />
+ </intent-filter>
+
+ <intent-filter android:autoVerify="true">
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="http" />
+ <data android:scheme="https" />
+ <data android:host="multiple.pm.server.android.com" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest2.xml b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest2.xml
new file mode 100644
index 0000000..087ef705
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest2.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2020 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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.pm.test.intent.verifier.target.two"
+ android:versionCode="1">
+
+ <application>
+ <activity android:name=".TargetActivity" android:exported="true">
+ <intent-filter android:autoVerify="true">
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="http" />
+ <data android:scheme="https" />
+ <data android:scheme="non_web_scheme" />
+ <data android:host="only_https_plus_non_web_scheme.pm.server.android.com" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest3.xml b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest3.xml
new file mode 100644
index 0000000..eb75b5e
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest3.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2020 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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.pm.test.intent.verifier.target.three"
+ android:versionCode="1">
+
+ <application>
+ <activity android:name=".TargetActivity" android:exported="true">
+ <intent-filter android:autoVerify="true">
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="http" />
+ <data android:host="multiple.pm.server.android.com" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4Base.xml b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4Base.xml
new file mode 100644
index 0000000..7eacb8b
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4Base.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2020 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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.pm.test.intent.verifier.target.four"
+ android:versionCode="1">
+
+ <application>
+ <activity android:name=".TargetActivity" android:exported="true">
+ <intent-filter android:autoVerify="true">
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="http" />
+ <data android:scheme="https" />
+ <data android:host="failing.pm.server.android.com" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4NoAutoVerify.xml b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4NoAutoVerify.xml
new file mode 100644
index 0000000..ecfee55
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4NoAutoVerify.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2020 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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.pm.test.intent.verifier.target.four"
+ android:versionCode="1">
+
+ <application>
+ <activity android:name=".TargetActivity" android:exported="true">
+ <intent-filter android:autoVerify="false">
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="http" />
+ <data android:scheme="https" />
+ <data android:host="failing.pm.server.android.com" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4Wildcard.xml b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4Wildcard.xml
new file mode 100644
index 0000000..0f0f53b
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4Wildcard.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2020 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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.pm.test.intent.verifier.target.four"
+ android:versionCode="1">
+
+ <application>
+ <activity android:name=".TargetActivity" android:exported="true">
+ <intent-filter android:autoVerify="true">
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="http" />
+ <data android:scheme="https" />
+ <data android:host="failing.pm.server.android.com" />
+ <data android:host="*.wildcard.tld" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4WildcardNoAutoVerify.xml b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4WildcardNoAutoVerify.xml
new file mode 100644
index 0000000..d5652e1
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4WildcardNoAutoVerify.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2020 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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.pm.test.intent.verifier.target.four"
+ android:versionCode="1">
+
+ <application>
+ <activity android:name=".TargetActivity" android:exported="true">
+ <intent-filter android:autoVerify="false">
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="http" />
+ <data android:scheme="https" />
+ <data android:host="failing.pm.server.android.com" />
+ <data android:host="*.wildcard.tld" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
diff --git a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
index 02d10e3..b7a36f2 100644
--- a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
@@ -223,9 +223,24 @@
}
@Test
- public void arePrimitivesSupported_withComposeCapability_returnsAlwaysTrue() {
+ public void arePrimitivesSupported_withNullResultFromNative_returnsAlwaysFalse() {
mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
- assertArrayEquals(new boolean[]{true, true},
+ when(mNativeWrapperMock.vibratorGetSupportedPrimitives()).thenReturn(null);
+
+ assertArrayEquals(new boolean[]{false, false},
+ createService().arePrimitivesSupported(new int[]{
+ VibrationEffect.Composition.PRIMITIVE_CLICK,
+ VibrationEffect.Composition.PRIMITIVE_QUICK_RISE
+ }));
+ }
+
+ @Test
+ public void arePrimitivesSupported_withSomeSupportedPrimitives_returnsBasedOnNativeResult() {
+ mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ when(mNativeWrapperMock.vibratorGetSupportedPrimitives())
+ .thenReturn(new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK});
+
+ assertArrayEquals(new boolean[]{true, false},
createService().arePrimitivesSupported(new int[]{
VibrationEffect.Composition.PRIMITIVE_CLICK,
VibrationEffect.Composition.PRIMITIVE_QUICK_RISE
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 8fc2287..7f6723e 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -149,6 +149,7 @@
public static final String NOT_PROFILE_OWNER_MSG = "does not own the profile";
public static final String NOT_ORG_OWNED_PROFILE_OWNER_MSG =
"not the profile owner on organization-owned device";
+ public static final String INVALID_CALLING_IDENTITY_MSG = "Calling identity is not authorized";
public static final String ONGOING_CALL_MSG = "ongoing call on the device";
// TODO replace all instances of this with explicit {@link #mServiceContext}.
@@ -2404,13 +2405,13 @@
// Set admin1 as DA.
dpm.setActiveAdmin(admin1, false);
assertTrue(dpm.isAdminActive(admin1));
- assertExpectException(SecurityException.class, /* messageRegex= */ NOT_DEVICE_OWNER_MSG,
- () -> dpm.reboot(admin1));
+ assertExpectException(SecurityException.class, /* messageRegex= */
+ INVALID_CALLING_IDENTITY_MSG, () -> dpm.reboot(admin1));
// Set admin1 as PO.
assertTrue(dpm.setProfileOwner(admin1, null, UserHandle.USER_SYSTEM));
- assertExpectException(SecurityException.class, /* messageRegex= */ NOT_DEVICE_OWNER_MSG,
- () -> dpm.reboot(admin1));
+ assertExpectException(SecurityException.class, /* messageRegex= */
+ INVALID_CALLING_IDENTITY_MSG, () -> dpm.reboot(admin1));
// Remove PO and add DO.
dpm.clearProfileOwner(admin1);
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index 0ea84da..26d46db 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -1276,7 +1276,8 @@
* @return The initialized AudioRecord
*/
private @NonNull AudioRecord createAudioRecordForEvent(
- @NonNull SoundTrigger.GenericRecognitionEvent event) {
+ @NonNull SoundTrigger.GenericRecognitionEvent event)
+ throws IllegalArgumentException, UnsupportedOperationException {
AudioAttributes.Builder attributesBuilder = new AudioAttributes.Builder();
attributesBuilder.setInternalCapturePreset(MediaRecorder.AudioSource.HOTWORD);
AudioAttributes attributes = attributesBuilder.build();
@@ -1285,21 +1286,15 @@
sEventLogger.log(new SoundTriggerLogger.StringEvent("createAudioRecordForEvent"));
- try {
- return (new AudioRecord.Builder())
- .setAudioAttributes(attributes)
- .setAudioFormat((new AudioFormat.Builder())
- .setChannelMask(originalFormat.getChannelMask())
- .setEncoding(originalFormat.getEncoding())
- .setSampleRate(originalFormat.getSampleRate())
- .build())
- .setSessionId(event.getCaptureSession())
- .build();
- } catch (IllegalArgumentException | UnsupportedOperationException e) {
- Slog.w(TAG, mPuuid + ": createAudioRecordForEvent(" + event
- + "), failed to create AudioRecord");
- return null;
- }
+ return (new AudioRecord.Builder())
+ .setAudioAttributes(attributes)
+ .setAudioFormat((new AudioFormat.Builder())
+ .setChannelMask(originalFormat.getChannelMask())
+ .setEncoding(originalFormat.getEncoding())
+ .setSampleRate(originalFormat.getSampleRate())
+ .build())
+ .setSessionId(event.getCaptureSession())
+ .build();
}
@Override
@@ -1325,13 +1320,13 @@
// execute if throttled:
() -> {
if (event.isCaptureAvailable()) {
- AudioRecord capturedData = createAudioRecordForEvent(event);
-
- // Currently we need to start and release the audio record to reset
- // the DSP even if we don't want to process the event
- if (capturedData != null) {
+ try {
+ AudioRecord capturedData = createAudioRecordForEvent(event);
capturedData.startRecording();
capturedData.release();
+ } catch (IllegalArgumentException | UnsupportedOperationException e) {
+ Slog.w(TAG, mPuuid + ": createAudioRecordForEvent(" + event
+ + "), failed to create AudioRecord");
}
}
}));
diff --git a/tests/AutoVerify/app1/Android.bp b/tests/AutoVerify/app1/Android.bp
deleted file mode 100644
index 548519f..0000000
--- a/tests/AutoVerify/app1/Android.bp
+++ /dev/null
@@ -1,11 +0,0 @@
-android_app {
- name: "AutoVerifyTest",
- srcs: ["src/**/*.java"],
- resource_dirs: ["res"],
- platform_apis: true,
- min_sdk_version: "26",
- target_sdk_version: "26",
- optimize: {
- enabled: false,
- },
-}
diff --git a/tests/AutoVerify/app1/AndroidManifest.xml b/tests/AutoVerify/app1/AndroidManifest.xml
deleted file mode 100644
index d9caad4..0000000
--- a/tests/AutoVerify/app1/AndroidManifest.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2020 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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.test.autoverify" >
-
- <uses-sdk android:targetSdkVersion="26" />
-
- <application
- android:label="@string/app_name" >
- <activity
- android:name=".MainActivity"
- android:label="@string/app_name" >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
-
- <intent-filter android:autoVerify="true">
- <action android:name="android.intent.action.VIEW" />
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.BROWSABLE" />
- <data android:scheme="http" />
- <data android:scheme="https" />
- <data android:host="explicit.example.com" />
- </intent-filter>
- </activity>
- </application>
-</manifest>
diff --git a/tests/AutoVerify/app1/res/values/strings.xml b/tests/AutoVerify/app1/res/values/strings.xml
deleted file mode 100644
index e234355..0000000
--- a/tests/AutoVerify/app1/res/values/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright 2020 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.
--->
-
-<resources>
- <!-- app icon label, do not translate -->
- <string name="app_name" translatable="false">AutoVerify Test</string>
-</resources>
diff --git a/tests/AutoVerify/app2/Android.bp b/tests/AutoVerify/app2/Android.bp
deleted file mode 100644
index 1c6c97b..0000000
--- a/tests/AutoVerify/app2/Android.bp
+++ /dev/null
@@ -1,11 +0,0 @@
-android_app {
- name: "AutoVerifyTest2",
- srcs: ["src/**/*.java"],
- resource_dirs: ["res"],
- platform_apis: true,
- min_sdk_version: "26",
- target_sdk_version: "26",
- optimize: {
- enabled: false,
- },
-}
diff --git a/tests/AutoVerify/app2/AndroidManifest.xml b/tests/AutoVerify/app2/AndroidManifest.xml
deleted file mode 100644
index a008078..0000000
--- a/tests/AutoVerify/app2/AndroidManifest.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2020 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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.test.autoverify" >
-
- <uses-sdk android:targetSdkVersion="26" />
-
- <application
- android:label="@string/app_name" >
- <activity
- android:name=".MainActivity"
- android:label="@string/app_name" >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
-
- <intent-filter android:autoVerify="true">
- <action android:name="android.intent.action.VIEW" />
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.BROWSABLE" />
- <data android:scheme="http" />
- <data android:scheme="https" />
- <data android:host="explicit.example.com" />
- <data android:host="*.wildcard.tld" />
- </intent-filter>
- </activity>
- </application>
-</manifest>
diff --git a/tests/AutoVerify/app2/res/values/strings.xml b/tests/AutoVerify/app2/res/values/strings.xml
deleted file mode 100644
index e234355..0000000
--- a/tests/AutoVerify/app2/res/values/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright 2020 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.
--->
-
-<resources>
- <!-- app icon label, do not translate -->
- <string name="app_name" translatable="false">AutoVerify Test</string>
-</resources>
diff --git a/tests/AutoVerify/app2/src/com/android/test/autoverify/MainActivity.java b/tests/AutoVerify/app2/src/com/android/test/autoverify/MainActivity.java
deleted file mode 100644
index 09ef472..0000000
--- a/tests/AutoVerify/app2/src/com/android/test/autoverify/MainActivity.java
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * Copyright (C) 2020 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.
- */
diff --git a/tests/AutoVerify/app3/Android.bp b/tests/AutoVerify/app3/Android.bp
deleted file mode 100644
index 70a2b77..0000000
--- a/tests/AutoVerify/app3/Android.bp
+++ /dev/null
@@ -1,11 +0,0 @@
-android_app {
- name: "AutoVerifyTest3",
- srcs: ["src/**/*.java"],
- resource_dirs: ["res"],
- platform_apis: true,
- min_sdk_version: "26",
- target_sdk_version: "26",
- optimize: {
- enabled: false,
- },
-}
diff --git a/tests/AutoVerify/app3/AndroidManifest.xml b/tests/AutoVerify/app3/AndroidManifest.xml
deleted file mode 100644
index efaabc9..0000000
--- a/tests/AutoVerify/app3/AndroidManifest.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2020 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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.test.autoverify" >
-
- <uses-sdk android:targetSdkVersion="26" />
-
- <application
- android:label="@string/app_name" >
- <activity
- android:name=".MainActivity"
- android:label="@string/app_name" >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
-
- <!-- does not request autoVerify -->
- <intent-filter>
- <action android:name="android.intent.action.VIEW" />
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.BROWSABLE" />
- <data android:scheme="http" />
- <data android:scheme="https" />
- <data android:host="explicit.example.com" />
- </intent-filter>
- </activity>
- </application>
-</manifest>
diff --git a/tests/AutoVerify/app3/res/values/strings.xml b/tests/AutoVerify/app3/res/values/strings.xml
deleted file mode 100644
index e234355..0000000
--- a/tests/AutoVerify/app3/res/values/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright 2020 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.
--->
-
-<resources>
- <!-- app icon label, do not translate -->
- <string name="app_name" translatable="false">AutoVerify Test</string>
-</resources>
diff --git a/tests/AutoVerify/app3/src/com/android/test/autoverify/MainActivity.java b/tests/AutoVerify/app3/src/com/android/test/autoverify/MainActivity.java
deleted file mode 100644
index 09ef472..0000000
--- a/tests/AutoVerify/app3/src/com/android/test/autoverify/MainActivity.java
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * Copyright (C) 2020 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.
- */
diff --git a/tests/AutoVerify/app4/Android.bp b/tests/AutoVerify/app4/Android.bp
deleted file mode 100644
index fbdae11..0000000
--- a/tests/AutoVerify/app4/Android.bp
+++ /dev/null
@@ -1,11 +0,0 @@
-android_app {
- name: "AutoVerifyTest4",
- srcs: ["src/**/*.java"],
- resource_dirs: ["res"],
- platform_apis: true,
- min_sdk_version: "26",
- target_sdk_version: "26",
- optimize: {
- enabled: false,
- },
-}
diff --git a/tests/AutoVerify/app4/AndroidManifest.xml b/tests/AutoVerify/app4/AndroidManifest.xml
deleted file mode 100644
index 1c975f8..0000000
--- a/tests/AutoVerify/app4/AndroidManifest.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2020 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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.test.autoverify" >
-
- <uses-sdk android:targetSdkVersion="26" />
-
- <application
- android:label="@string/app_name" >
- <activity
- android:name=".MainActivity"
- android:label="@string/app_name" >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
-
- <!-- intentionally does not autoVerify -->
- <intent-filter>
- <action android:name="android.intent.action.VIEW" />
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.BROWSABLE" />
- <data android:scheme="http" />
- <data android:scheme="https" />
- <data android:host="explicit.example.com" />
- <data android:host="*.wildcard.tld" />
- </intent-filter>
- </activity>
- </application>
-</manifest>
diff --git a/tests/AutoVerify/app4/res/values/strings.xml b/tests/AutoVerify/app4/res/values/strings.xml
deleted file mode 100644
index e234355..0000000
--- a/tests/AutoVerify/app4/res/values/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright 2020 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.
--->
-
-<resources>
- <!-- app icon label, do not translate -->
- <string name="app_name" translatable="false">AutoVerify Test</string>
-</resources>
diff --git a/tests/AutoVerify/app4/src/com/android/test/autoverify/MainActivity.java b/tests/AutoVerify/app4/src/com/android/test/autoverify/MainActivity.java
deleted file mode 100644
index 09ef472..0000000
--- a/tests/AutoVerify/app4/src/com/android/test/autoverify/MainActivity.java
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * Copyright (C) 2020 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.
- */
diff --git a/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java b/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java
index c2a5459..f80af03 100644
--- a/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java
+++ b/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java
@@ -62,12 +62,22 @@
private final RebootStrategy mRebootStrategy;
private final TearDownRule mTearDownRule;
+ // When debugging, it may be useful to run a test case without rebooting the device afterwards,
+ // to manually verify the device state.
+ private boolean mDebugSkipAfterReboot;
+
public SystemPreparer(TemporaryFolder hostTempFolder, DeviceProvider deviceProvider) {
this(hostTempFolder, RebootStrategy.FULL, null, deviceProvider);
}
public SystemPreparer(TemporaryFolder hostTempFolder, RebootStrategy rebootStrategy,
@Nullable TestRuleDelegate testRuleDelegate, DeviceProvider deviceProvider) {
+ this(hostTempFolder, rebootStrategy, testRuleDelegate, false, deviceProvider);
+ }
+
+ public SystemPreparer(TemporaryFolder hostTempFolder, RebootStrategy rebootStrategy,
+ @Nullable TestRuleDelegate testRuleDelegate, boolean debugSkipAfterReboot,
+ DeviceProvider deviceProvider) {
mHostTempFolder = hostTempFolder;
mDeviceProvider = deviceProvider;
mRebootStrategy = rebootStrategy;
@@ -75,6 +85,7 @@
if (testRuleDelegate != null) {
testRuleDelegate.setDelegate(mTearDownRule);
}
+ mDebugSkipAfterReboot = debugSkipAfterReboot;
}
/** Copies a file within the host test jar to a path on device. */
@@ -172,7 +183,9 @@
case USERSPACE_UNTIL_ONLINE:
device.rebootUserspaceUntilOnline();
break;
- case START_STOP:
+ // TODO(b/159540015): Make this START_STOP instead of default once it's fixed. Can't
+ // currently be done because START_STOP is commented out.
+ default:
device.executeShellCommand("stop");
device.executeShellCommand("start");
ITestDevice.RecoveryMode cachedRecoveryMode = device.getRecoveryMode();
@@ -228,7 +241,9 @@
for (final String packageName : mInstalledPackages) {
device.uninstallPackage(packageName);
}
- reboot();
+ if (!mDebugSkipAfterReboot) {
+ reboot();
+ }
} catch (DeviceNotAvailableException e) {
Assert.fail(e.toString());
}
@@ -355,6 +370,7 @@
/**
* How to reboot the device. Ordered from slowest to fastest.
*/
+ @SuppressWarnings("DanglingJavadoc")
public enum RebootStrategy {
/** @see ITestDevice#reboot() */
FULL,
@@ -374,7 +390,15 @@
*
* TODO(b/159540015): There's a bug with this causing unnecessary disk space usage, which
* can eventually lead to an insufficient storage space error.
+ *
+ * This can be uncommented for local development, but should be left out when merging.
+ * It is done this way to hopefully be caught by code review, since merging this will
+ * break all of postsubmit. But the nearly 50% reduction in test runtime is worth having
+ * this option exist.
+ *
+ * @deprecated do not use this in merged code until bug is resolved
*/
- START_STOP
+// @Deprecated
+// START_STOP
}
}