Merge "Fixed an issue where the keyguard statusbar was faded" into qt-r1-dev
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 6e97076..55e21a4 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -146,14 +146,6 @@
public static final String ASSIST_HANDLES_SHOWN_FREQUENCY_THRESHOLD_MS =
"assist_handles_shown_frequency_threshold_ms";
- // Flag related to clock face
-
- /**
- * (String) Contains the clock plugin service names that are not allow to be shown.
- * Each service name is seperated by a comma(",") in the string.
- */
- public static final String CLOCK_FACE_BLACKLIST = "clock_face_blacklist";
-
/**
* (long) How long, in milliseconds, for teaching behaviors to wait before considering the user
* taught.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 45f69d6..0a834c8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1683,12 +1683,12 @@
strongAuth == StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT
|| strongAuth == StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
+ boolean canBypass = mKeyguardBypassController != null
+ && mKeyguardBypassController.canBypass();
// There's no reason to ask the HAL for authentication when the user can dismiss the
// bouncer, unless we're bypassing and need to auto-dismiss the lock screen even when
// TrustAgents or biometrics are keeping the device unlocked.
- boolean bypassEnabled = mKeyguardBypassController != null
- && mKeyguardBypassController.getBypassEnabled();
- boolean becauseCannotSkipBouncer = !getUserCanSkipBouncer(user) || bypassEnabled;
+ boolean becauseCannotSkipBouncer = !getUserCanSkipBouncer(user) || canBypass;
// Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an
// instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware.
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
index 9edb54c..9e2464e 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
@@ -15,8 +15,6 @@
*/
package com.android.keyguard.clock;
-import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.CLOCK_FACE_BLACKLIST;
-
import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.Context;
@@ -26,12 +24,9 @@
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
-import android.provider.DeviceConfig;
import android.provider.Settings;
import android.util.ArrayMap;
-import android.util.ArraySet;
import android.util.DisplayMetrics;
-import android.util.Log;
import android.view.LayoutInflater;
import androidx.annotation.VisibleForTesting;
@@ -47,12 +42,10 @@
import com.android.systemui.util.InjectionInflationController;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Supplier;
-import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -74,8 +67,6 @@
private final Handler mMainHandler = new Handler(Looper.getMainLooper());
private final CurrentUserObservable mCurrentUserObservable;
- private final ArraySet<String> mBlacklistedClockPlugins = new ArraySet<>();
-
/**
* Observe settings changes to know when to switch the clock face.
*/
@@ -164,41 +155,6 @@
DisplayMetrics dm = res.getDisplayMetrics();
mWidth = dm.widthPixels;
mHeight = dm.heightPixels;
-
- updateBlackList();
- registerDeviceConfigListener();
- }
-
- private void updateBlackList() {
- String blacklist = getBlackListFromConfig();
-
- mBlacklistedClockPlugins.clear();
- if (blacklist != null && !blacklist.isEmpty()) {
- mBlacklistedClockPlugins.addAll(Arrays.asList(blacklist.split(",")));
- }
- }
-
- String getBlackListFromConfig() {
- return DeviceConfig.getString(
- DeviceConfig.NAMESPACE_SYSTEMUI, CLOCK_FACE_BLACKLIST, null);
- }
-
- private void registerDeviceConfigListener() {
- DeviceConfig.addOnPropertiesChangedListener(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- r -> mMainHandler.post(r),
- properties -> onDeviceConfigPropertiesChanged(properties.getNamespace()));
- }
-
- void onDeviceConfigPropertiesChanged(String namespace) {
- if (!DeviceConfig.NAMESPACE_SYSTEMUI.equals(namespace)) {
- Log.e(TAG, "Received update from DeviceConfig for unrelated namespace: "
- + namespace);
- return;
- }
-
- updateBlackList();
- reload();
}
/**
@@ -350,12 +306,10 @@
}
/**
- * Get information about clock faces which are available and not in blacklist.
+ * Get information about available clock faces.
*/
List<ClockInfo> getInfo() {
- return mClockInfo.stream()
- .filter(info -> !mBlacklistedClockPlugins.contains(info.getId()))
- .collect(Collectors.toList());
+ return mClockInfo;
}
/**
@@ -407,7 +361,7 @@
if (ClockManager.this.isDocked()) {
final String name = mSettingsWrapper.getDockedClockFace(
mCurrentUserObservable.getCurrentUser().getValue());
- if (name != null && !mBlacklistedClockPlugins.contains(name)) {
+ if (name != null) {
plugin = mClocks.get(name);
if (plugin != null) {
return plugin;
@@ -416,7 +370,7 @@
}
final String name = mSettingsWrapper.getLockScreenCustomClockFace(
mCurrentUserObservable.getCurrentUser().getValue());
- if (name != null && !mBlacklistedClockPlugins.contains(name)) {
+ if (name != null) {
plugin = mClocks.get(name);
}
return plugin;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 787cc97..aeb8574 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -16,8 +16,11 @@
package com.android.systemui.statusbar;
+import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
+
import android.content.Context;
import android.content.res.Resources;
+import android.os.Handler;
import android.os.Trace;
import android.os.UserHandle;
import android.util.Log;
@@ -44,6 +47,7 @@
import java.util.Stack;
import javax.inject.Inject;
+import javax.inject.Named;
import javax.inject.Singleton;
import dagger.Lazy;
@@ -59,6 +63,8 @@
public class NotificationViewHierarchyManager implements DynamicPrivacyController.Listener {
private static final String TAG = "NotificationViewHierarchyManager";
+ private final Handler mHandler;
+
//TODO: change this top <Entry, List<Entry>>?
private final HashMap<ExpandableNotificationRow, List<ExpandableNotificationRow>>
mTmpChildOrderMap = new HashMap<>();
@@ -88,9 +94,13 @@
// Used to help track down re-entrant calls to our update methods, which will cause bugs.
private boolean mPerformingUpdate;
+ // Hack to get around re-entrant call in onDynamicPrivacyChanged() until we can track down
+ // the problem.
+ private boolean mIsHandleDynamicPrivacyChangeScheduled;
@Inject
public NotificationViewHierarchyManager(Context context,
+ @Named(MAIN_HANDLER_NAME) Handler mainHandler,
NotificationLockscreenUserManager notificationLockscreenUserManager,
NotificationGroupManager groupManager,
VisualStabilityManager visualStabilityManager,
@@ -100,6 +110,7 @@
BubbleData bubbleData,
KeyguardBypassController bypassController,
DynamicPrivacyController privacyController) {
+ mHandler = mainHandler;
mLockscreenUserManager = notificationLockscreenUserManager;
mBypassController = bypassController;
mGroupManager = groupManager;
@@ -438,6 +449,20 @@
@Override
public void onDynamicPrivacyChanged() {
+ if (mPerformingUpdate) {
+ Log.w(TAG, "onDynamicPrivacyChanged made a re-entrant call");
+ }
+ // This listener can be called from updateNotificationViews() via a convoluted listener
+ // chain, so we post here to prevent a re-entrant call. See b/136186188
+ // TODO: Refactor away the need for this
+ if (!mIsHandleDynamicPrivacyChangeScheduled) {
+ mIsHandleDynamicPrivacyChangeScheduled = true;
+ mHandler.post(this::onHandleDynamicPrivacyChanged);
+ }
+ }
+
+ private void onHandleDynamicPrivacyChanged() {
+ mIsHandleDynamicPrivacyChangeScheduled = false;
updateNotificationViews();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
index 3f1cb6c..af23ac3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
@@ -104,23 +104,11 @@
*/
fun onBiometricAuthenticated(biometricSourceType: BiometricSourceType): Boolean {
if (bypassEnabled) {
- if (bouncerShowing) {
- // Whenever the bouncer is showing, we want to unlock. Otherwise we can get stuck
- // in the shade locked where the bouncer wouldn't unlock
- return true
- }
- if (statusBarStateController.state != StatusBarState.KEYGUARD) {
- // We're bypassing but not actually on the lockscreen, the user should decide when
- // to unlock
- return false
- }
- if (launchingAffordance) {
- return false
- }
- if (isPulseExpanding || qSExpanded) {
+ val can = canBypass()
+ if (!can && (isPulseExpanding || qSExpanded)) {
pendingUnlockType = biometricSourceType
- return false
}
+ return can
}
return true
}
@@ -134,6 +122,22 @@
}
}
+ /**
+ * If keyguard can be dismissed because of bypass.
+ */
+ fun canBypass(): Boolean {
+ if (bypassEnabled) {
+ return when {
+ bouncerShowing -> true
+ statusBarStateController.state != StatusBarState.KEYGUARD -> false
+ launchingAffordance -> false
+ isPulseExpanding || qSExpanded -> false
+ else -> true
+ }
+ }
+ return false
+ }
+
fun onStartedGoingToSleep() {
pendingUnlockType = null
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 6208ab8..2e02fd5 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -52,6 +52,7 @@
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.TelephonyIntents;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
import org.junit.Assert;
import org.junit.Before;
@@ -88,6 +89,8 @@
private UserManager mUserManager;
@Mock
private DevicePolicyManager mDevicePolicyManager;
+ @Mock
+ private KeyguardBypassController mKeyguardBypassController;
private TestableLooper mTestableLooper;
private TestableKeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -332,6 +335,28 @@
}
@Test
+ public void testTriesToAuthenticate_whenTrustOnAgentKeyguard_ifBypass() {
+ mKeyguardUpdateMonitor.setKeyguardBypassController(mKeyguardBypassController);
+ mKeyguardUpdateMonitor.dispatchStartedWakingUp();
+ mTestableLooper.processAllMessages();
+ when(mKeyguardBypassController.canBypass()).thenReturn(true);
+ mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */,
+ KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */);
+ mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
+ verify(mFaceManager).authenticate(any(), any(), anyInt(), any(), any(), anyInt());
+ }
+
+ @Test
+ public void testIgnoresAuth_whenTrustAgentOnKeyguard_withoutBypass() {
+ mKeyguardUpdateMonitor.dispatchStartedWakingUp();
+ mTestableLooper.processAllMessages();
+ mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */,
+ KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */);
+ mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
+ verify(mFaceManager, never()).authenticate(any(), any(), anyInt(), any(), any(), anyInt());
+ }
+
+ @Test
public void testOnFaceAuthenticated_skipsFaceWhenAuthenticated() {
mKeyguardUpdateMonitor.onFaceAuthenticated(KeyguardUpdateMonitor.getCurrentUser());
mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(true);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
index 6891f56..3330d1e 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
@@ -20,14 +20,12 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.ContentResolver;
import android.database.ContentObserver;
import android.net.Uri;
-import android.provider.DeviceConfig;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
@@ -52,8 +50,6 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.List;
-
@SmallTest
@RunWith(AndroidTestingRunner.class)
// Need to run tests on main looper because LiveData operations such as setData, observe,
@@ -67,7 +63,7 @@
private static final int SECONDARY_USER_ID = 11;
private static final Uri SETTINGS_URI = null;
- ClockManager mClockManager;
+ private ClockManager mClockManager;
private ContentObserver mContentObserver;
private DockManagerFake mFakeDockManager;
private MutableLiveData<Integer> mCurrentUser;
@@ -144,33 +140,6 @@
}
@Test
- public void getCurrentClock_inBlackList() {
- mClockManager = spy(mClockManager);
- // GIVEN that settings is set to the bubble clock face
- when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(BUBBLE_CLOCK);
- // WHEN settings change event is fired
- mContentObserver.onChange(false, SETTINGS_URI, MAIN_USER_ID);
- // GIVEN that bubble clock is in blacklist
- when(mClockManager.getBlackListFromConfig()).thenReturn(BUBBLE_CLOCK);
- // WHEN device config change of systemui is fired
- mClockManager.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI);
- // THEN the result is null, indicated the current clock should be reset to the default one.
- assertThat(mClockManager.getCurrentClock()).isNull();
- }
-
- @Test
- public void getClockInfo_inBlackList() {
- mClockManager = spy(mClockManager);
- // GIVEN that bubble clock is in blacklist
- when(mClockManager.getBlackListFromConfig()).thenReturn(BUBBLE_CLOCK);
- // WHEN device config change of systemui is fired
- mClockManager.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI);
- // THEN the ClockInfo should not contain bubble clock
- List<ClockInfo> clocks = mClockManager.getClockInfos();
- assertThat(clocks.stream().anyMatch(info -> BUBBLE_CLOCK.equals(info.getId()))).isFalse();
- }
-
- @Test
public void onClockChanged_customClock() {
// GIVEN that settings is set to the bubble clock face
when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(BUBBLE_CLOCK);
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 010b85e..58fb53a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -20,12 +20,16 @@
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.os.Handler;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.View;
@@ -79,13 +83,19 @@
@Mock private VisualStabilityManager mVisualStabilityManager;
@Mock private ShadeController mShadeController;
+ private TestableLooper mTestableLooper;
+ private Handler mHandler;
private NotificationViewHierarchyManager mViewHierarchyManager;
private NotificationTestHelper mHelper;
+ private boolean mMadeReentrantCall = false;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- Assert.sMainLooper = TestableLooper.get(this).getLooper();
+ mTestableLooper = TestableLooper.get(this);
+ Assert.sMainLooper = mTestableLooper.getLooper();
+ mHandler = Handler.createAsync(mTestableLooper.getLooper());
+
mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
mDependency.injectTestDependency(NotificationLockscreenUserManager.class,
mLockscreenUserManager);
@@ -98,7 +108,7 @@
when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
mViewHierarchyManager = new NotificationViewHierarchyManager(mContext,
- mLockscreenUserManager, mGroupManager, mVisualStabilityManager,
+ mHandler, mLockscreenUserManager, mGroupManager, mVisualStabilityManager,
mock(StatusBarStateControllerImpl.class), mEntryManager,
() -> mShadeController, new BubbleData(mContext),
mock(KeyguardBypassController.class),
@@ -215,9 +225,60 @@
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;
+ doAnswer((invocation) -> {
+ if (!mMadeReentrantCall) {
+ mMadeReentrantCall = true;
+ mViewHierarchyManager.onDynamicPrivacyChanged();
+ }
+ return null;
+ }).when(mListContainer).setMaxDisplayedNotifications(anyInt());
+
+ // WHEN we call updateNotificationViews()
+ mViewHierarchyManager.updateNotificationViews();
+
+ // THEN onNotificationViewUpdateFinished() is only called once
+ verify(mListContainer).onNotificationViewUpdateFinished();
+
+ // WHEN we drain the looper
+ mTestableLooper.processAllMessages();
+
+ // THEN updateNotificationViews() is called a second time (for the reentrant call)
+ verify(mListContainer, times(2)).onNotificationViewUpdateFinished();
+ }
+
+ @Test
+ public void testMultipleReentrantCallsToOnDynamicPrivacyChangedOnlyPostOnce() {
+ // GIVEN a ListContainer that will make many re-entrant calls to updateNotificationViews()
+ mMadeReentrantCall = false;
+ doAnswer((invocation) -> {
+ if (!mMadeReentrantCall) {
+ mMadeReentrantCall = true;
+ mViewHierarchyManager.onDynamicPrivacyChanged();
+ mViewHierarchyManager.onDynamicPrivacyChanged();
+ mViewHierarchyManager.onDynamicPrivacyChanged();
+ mViewHierarchyManager.onDynamicPrivacyChanged();
+ }
+ return null;
+ }).when(mListContainer).setMaxDisplayedNotifications(anyInt());
+
+ // WHEN we call updateNotificationViews() and drain the looper
+ mViewHierarchyManager.updateNotificationViews();
+ verify(mListContainer).onNotificationViewUpdateFinished();
+ clearInvocations(mListContainer);
+ mTestableLooper.processAllMessages();
+
+ // THEN updateNotificationViews() is called only one more time
+ verify(mListContainer).onNotificationViewUpdateFinished();
+ }
+
private class FakeListContainer implements NotificationListContainer {
final LinearLayout mLayout = new LinearLayout(mContext);
final List<View> mRows = Lists.newArrayList();
+ private boolean mMakeReentrantCallDuringSetMaxDisplayedNotifications;
@Override
public void setChildTransferInProgress(boolean childTransferInProgress) {}
@@ -266,7 +327,11 @@
}
@Override
- public void setMaxDisplayedNotifications(int maxNotifications) {}
+ public void setMaxDisplayedNotifications(int maxNotifications) {
+ if (mMakeReentrantCallDuringSetMaxDisplayedNotifications) {
+ mViewHierarchyManager.onDynamicPrivacyChanged();
+ }
+ }
@Override
public ViewGroup getViewParentForNotification(NotificationEntry entry) {
@@ -301,5 +366,7 @@
return false;
}
+ @Override
+ public void onNotificationViewUpdateFinished() { }
}
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index e2b59b4..c2f4529 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -108,7 +108,7 @@
.replaceWith("?");
private ByteBuffer mUtf8BufferStat = ByteBuffer.allocateDirect(MAX_LOW_POWER_STATS_SIZE);
private CharBuffer mUtf16BufferStat = CharBuffer.allocate(MAX_LOW_POWER_STATS_SIZE);
- private static final int MAX_LOW_POWER_STATS_SIZE = 2048;
+ private static final int MAX_LOW_POWER_STATS_SIZE = 4096;
/**
* Replaces the information in the given rpmStats with up-to-date information.
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 42802f6..d1b5534 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -1208,14 +1208,22 @@
}
}
- // Traffic occurring on stacked interfaces is usually clatd,
- // which is already accounted against its final egress interface
- // by the kernel. Thus, we only need to collect stacked
- // interface stats at the UID level.
+ // Traffic occurring on stacked interfaces is usually clatd.
+ // UID stats are always counted on the stacked interface and never
+ // on the base interface, because the packets on the base interface
+ // do not actually match application sockets until they are translated.
+ //
+ // Interface stats are more complicated. Packets subject to BPF offload
+ // never appear on the base interface and only appear on the stacked
+ // interface, so to ensure those packets increment interface stats, interface
+ // stats from stacked interfaces must be collected.
final List<LinkProperties> stackedLinks = state.linkProperties.getStackedLinks();
for (LinkProperties stackedLink : stackedLinks) {
final String stackedIface = stackedLink.getInterfaceName();
if (stackedIface != null) {
+ if (mUseBpfTrafficStats) {
+ findOrCreateNetworkIdentitySet(mActiveIfaces, stackedIface).add(ident);
+ }
findOrCreateNetworkIdentitySet(mActiveUidIfaces, stackedIface).add(ident);
if (isMobile) {
mobileIfaces.add(stackedIface);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 0faea61..bc67cb0 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -3162,7 +3162,7 @@
boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow) {
return ensureActivityConfiguration(globalChanges, preserveWindow,
- false /* ignoreStopState */);
+ false /* ignoreVisibility */);
}
/**
@@ -3172,15 +3172,15 @@
* @param globalChanges The changes to the global configuration.
* @param preserveWindow If the activity window should be preserved on screen if the activity
* is relaunched.
- * @param ignoreStopState If we should try to relaunch the activity even if it is in the stopped
- * state. This is useful for the case where we know the activity will be
- * visible soon and we want to ensure its configuration before we make it
- * visible.
+ * @param ignoreVisibility If we should try to relaunch the activity even if it is invisible
+ * (stopped state). This is useful for the case where we know the
+ * activity will be visible soon and we want to ensure its configuration
+ * before we make it visible.
* @return False if the activity was relaunched and true if it wasn't relaunched because we
* can't or the app handles the specific configuration that is changing.
*/
boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow,
- boolean ignoreStopState) {
+ boolean ignoreVisibility) {
final ActivityStack stack = getActivityStack();
if (stack.mConfigWillChange) {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
@@ -3196,15 +3196,9 @@
return true;
}
- if (!ignoreStopState && (mState == STOPPING || mState == STOPPED)) {
+ if (!ignoreVisibility && (mState == STOPPING || mState == STOPPED || !shouldBeVisible())) {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
- "Skipping config check stopped or stopping: " + this);
- return true;
- }
-
- if (!shouldBeVisible()) {
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
- "Skipping config check invisible stack: " + this);
+ "Skipping config check invisible: " + this);
return true;
}
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index c4e0db46..2ecacbf 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -1880,8 +1880,7 @@
mRootActivityContainer.ensureActivitiesVisible(resuming, 0, !PRESERVE_WINDOWS);
}
- private void addToStopping(ActivityRecord r, boolean scheduleIdle, boolean idleDelayed,
- String reason) {
+ void addToStopping(ActivityRecord r, boolean scheduleIdle, boolean idleDelayed, String reason) {
if (!mStackSupervisor.mStoppingActivities.contains(r)) {
EventLog.writeEvent(EventLogTags.AM_ADD_TO_STOPPING, r.mUserId,
System.identityHashCode(r), r.shortComponentName, reason);
@@ -2176,7 +2175,7 @@
// sure it matches the current configuration.
if (r != starting && notifyClients) {
r.ensureActivityConfiguration(0 /* globalChanges */, preserveWindows,
- true /* ignoreStopState */);
+ true /* ignoreVisibility */);
}
if (!r.attachedToProcess()) {
@@ -3138,8 +3137,10 @@
boolean newTask, boolean keepCurTransition, ActivityOptions options) {
TaskRecord rTask = r.getTaskRecord();
final int taskId = rTask.taskId;
+ final boolean allowMoveToFront = options == null || !options.getAvoidMoveToFront();
// mLaunchTaskBehind tasks get placed at the back of the task stack.
- if (!r.mLaunchTaskBehind && (taskForIdLocked(taskId) == null || newTask)) {
+ if (!r.mLaunchTaskBehind && allowMoveToFront
+ && (taskForIdLocked(taskId) == null || newTask)) {
// Last activity in task had been removed or ActivityManagerService is reusing task.
// Insert or replace.
// Might not even be in.
@@ -3198,7 +3199,9 @@
task.setFrontOfTask();
- if (!isHomeOrRecentsStack() || numActivities() > 0) {
+ // The transition animation and starting window are not needed if {@code allowMoveToFront}
+ // is false, because the activity won't be visible.
+ if ((!isHomeOrRecentsStack() || numActivities() > 0) && allowMoveToFront) {
final DisplayContent dc = getDisplay().mDisplayContent;
if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
"Prepare open transition: starting " + r);
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 0a88eef..eda9d24 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2307,12 +2307,12 @@
// isLockTaskModeViolation fails below.
if (mReuseTask == null) {
+ final boolean toTop = !mLaunchTaskBehind && !mAvoidMoveToFront;
final TaskRecord task = mTargetStack.createTaskRecord(
mSupervisor.getNextTaskIdForUserLocked(mStartActivity.mUserId),
mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info,
mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession,
- mVoiceInteractor, !mLaunchTaskBehind /* toTop */, mStartActivity, mSourceRecord,
- mOptions);
+ mVoiceInteractor, toTop, mStartActivity, mSourceRecord, mOptions);
addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask - mReuseTask");
updateBounds(mStartActivity.getTaskRecord(), mLaunchParams.mBounds);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index a70ea60..0f96f99 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1451,9 +1451,15 @@
.execute();
}
+ /**
+ * Start the recents activity to perform the recents animation.
+ *
+ * @param intent The intent to start the recents activity.
+ * @param recentsAnimationRunner Pass {@code null} to only preload the activity.
+ */
@Override
- public void startRecentsActivity(Intent intent, IAssistDataReceiver assistDataReceiver,
- IRecentsAnimationRunner recentsAnimationRunner) {
+ public void startRecentsActivity(Intent intent, @Deprecated IAssistDataReceiver unused,
+ @Nullable IRecentsAnimationRunner recentsAnimationRunner) {
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "startRecentsActivity()");
final int callingPid = Binder.getCallingPid();
final long origId = Binder.clearCallingIdentity();
@@ -1464,9 +1470,13 @@
// Start a new recents animation
final RecentsAnimation anim = new RecentsAnimation(this, mStackSupervisor,
- getActivityStartController(), mWindowManager, callingPid);
- anim.startRecentsActivity(intent, recentsAnimationRunner, recentsComponent,
- recentsUid, assistDataReceiver);
+ getActivityStartController(), mWindowManager, intent, recentsComponent,
+ recentsUid, callingPid);
+ if (recentsAnimationRunner == null) {
+ anim.preloadRecentsActivity();
+ } else {
+ anim.startRecentsActivity(recentsAnimationRunner);
+ }
}
} finally {
Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 434239f..bf627ec 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -34,7 +34,6 @@
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RECENTS_ANIMATIONS;
import android.app.ActivityOptions;
-import android.app.IAssistDataReceiver;
import android.content.ComponentName;
import android.content.Intent;
import android.os.RemoteException;
@@ -58,7 +57,12 @@
private final ActivityStartController mActivityStartController;
private final WindowManagerService mWindowManager;
private final ActivityDisplay mDefaultDisplay;
+ private final Intent mTargetIntent;
+ private final ComponentName mRecentsComponent;
+ private final int mRecentsUid;
private final int mCallingPid;
+ private final int mUserId;
+ private final int mTargetActivityType;
/**
* The activity which has been launched behind. We need to remember the activity because the
@@ -66,27 +70,90 @@
* for the exact activity.
*/
private ActivityRecord mLaunchedTargetActivity;
- private int mTargetActivityType;
// The stack to restore the target stack behind when the animation is finished
private ActivityStack mRestoreTargetBehindStack;
RecentsAnimation(ActivityTaskManagerService atm, ActivityStackSupervisor stackSupervisor,
ActivityStartController activityStartController, WindowManagerService wm,
- int callingPid) {
+ Intent targetIntent, ComponentName recentsComponent, int recentsUid, int callingPid) {
mService = atm;
mStackSupervisor = stackSupervisor;
mDefaultDisplay = mService.mRootActivityContainer.getDefaultDisplay();
mActivityStartController = activityStartController;
mWindowManager = wm;
+ mTargetIntent = targetIntent;
+ mRecentsComponent = recentsComponent;
+ mRecentsUid = recentsUid;
mCallingPid = callingPid;
+ mUserId = atm.getCurrentUserId();
+ mTargetActivityType = targetIntent.getComponent() != null
+ && recentsComponent.equals(targetIntent.getComponent())
+ ? ACTIVITY_TYPE_RECENTS
+ : ACTIVITY_TYPE_HOME;
}
- void startRecentsActivity(Intent intent, IRecentsAnimationRunner recentsAnimationRunner,
- ComponentName recentsComponent, int recentsUid,
- @Deprecated IAssistDataReceiver assistDataReceiver) {
- if (DEBUG) Slog.d(TAG, "startRecentsActivity(): intent=" + intent
- + " assistDataReceiver=" + assistDataReceiver);
+ /**
+ * Starts the recents activity in background without animation if the record doesn't exist or
+ * the client isn't launched. If the recents activity is already alive, ensure its configuration
+ * is updated to the current one.
+ */
+ void preloadRecentsActivity() {
+ if (DEBUG) Slog.d(TAG, "Preload recents with " + mTargetIntent);
+ ActivityStack targetStack = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED,
+ mTargetActivityType);
+ ActivityRecord targetActivity = getTargetActivity(targetStack);
+ if (targetActivity != null) {
+ if (targetActivity.visible || targetActivity.isTopRunningActivity()) {
+ // The activity is ready.
+ return;
+ }
+ if (targetActivity.attachedToProcess()) {
+ // The activity may be relaunched if it cannot handle the current configuration
+ // changes. The activity will be paused state if it is relaunched, otherwise it
+ // keeps the original stopped state.
+ targetActivity.ensureActivityConfiguration(0 /* globalChanges */,
+ false /* preserveWindow */, true /* ignoreVisibility */);
+ if (DEBUG) Slog.d(TAG, "Updated config=" + targetActivity.getConfiguration());
+ }
+ } else {
+ // Create the activity record. Because the activity is invisible, this doesn't really
+ // start the client.
+ startRecentsActivityInBackground("preloadRecents");
+ targetStack = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED, mTargetActivityType);
+ targetActivity = getTargetActivity(targetStack);
+ if (targetActivity == null) {
+ Slog.w(TAG, "Cannot start " + mTargetIntent);
+ return;
+ }
+ }
+
+ if (!targetActivity.attachedToProcess()) {
+ if (DEBUG) Slog.d(TAG, "Real start recents");
+ mStackSupervisor.startSpecificActivityLocked(targetActivity, false /* andResume */,
+ false /* checkConfig */);
+ // Make sure the activity won't be involved in transition.
+ if (targetActivity.mAppWindowToken != null) {
+ targetActivity.mAppWindowToken.getDisplayContent().mUnknownAppVisibilityController
+ .appRemovedOrHidden(targetActivity.mAppWindowToken);
+ }
+ }
+
+ // Invisible activity should be stopped. If the recents activity is alive and its doesn't
+ // need to relaunch by current configuration, then it may be already in stopped state.
+ if (!targetActivity.isState(ActivityStack.ActivityState.STOPPING,
+ ActivityStack.ActivityState.STOPPED)) {
+ // Add to stopping instead of stop immediately. So the client has the chance to perform
+ // traversal in non-stopped state (ViewRootImpl.mStopped) that would initialize more
+ // things (e.g. the measure can be done earlier). The actual stop will be performed when
+ // it reports idle.
+ targetStack.addToStopping(targetActivity, true /* scheduleIdle */,
+ true /* idleDelayed */, "preloadRecents");
+ }
+ }
+
+ void startRecentsActivity(IRecentsAnimationRunner recentsAnimationRunner) {
+ if (DEBUG) Slog.d(TAG, "startRecentsActivity(): intent=" + mTargetIntent);
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "RecentsAnimation#startRecentsActivity");
// TODO(multi-display) currently only support recents animation in default display.
@@ -100,15 +167,9 @@
}
// If the activity is associated with the recents stack, then try and get that first
- final int userId = mService.getCurrentUserId();
- mTargetActivityType = intent.getComponent() != null
- && recentsComponent.equals(intent.getComponent())
- ? ACTIVITY_TYPE_RECENTS
- : ACTIVITY_TYPE_HOME;
ActivityStack targetStack = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED,
mTargetActivityType);
- ActivityRecord targetActivity = getTargetActivity(targetStack, intent.getComponent(),
- userId);
+ ActivityRecord targetActivity = getTargetActivity(targetStack);
final boolean hasExistingActivity = targetActivity != null;
if (hasExistingActivity) {
final ActivityDisplay display = targetActivity.getDisplay();
@@ -127,7 +188,7 @@
true /* forceSend */, targetActivity);
}
- mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunching(intent);
+ mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunching(mTargetIntent);
mService.mH.post(() -> mService.mAmInternal.setRunningRemoteAnimation(mCallingPid, true));
@@ -148,23 +209,12 @@
}
} else {
// No recents activity, create the new recents activity bottom most
- ActivityOptions options = ActivityOptions.makeBasic();
- options.setLaunchActivityType(mTargetActivityType);
- options.setAvoidMoveToFront();
- intent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NO_ANIMATION);
-
- mActivityStartController
- .obtainStarter(intent, "startRecentsActivity_noTargetActivity")
- .setCallingUid(recentsUid)
- .setCallingPackage(recentsComponent.getPackageName())
- .setActivityOptions(SafeActivityOptions.fromBundle(options.toBundle()))
- .setMayWait(userId)
- .execute();
+ startRecentsActivityInBackground("startRecentsActivity_noTargetActivity");
// Move the recents activity into place for the animation
targetStack = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED,
mTargetActivityType);
- targetActivity = getTargetActivity(targetStack, intent.getComponent(), userId);
+ targetActivity = getTargetActivity(targetStack);
mDefaultDisplay.moveStackBehindBottomMostVisibleStack(targetStack);
if (DEBUG) {
Slog.d(TAG, "Moved stack=" + targetStack + " behind stack="
@@ -176,7 +226,7 @@
// TODO: Maybe wait for app to draw in this particular case?
- if (DEBUG) Slog.d(TAG, "Started intent=" + intent);
+ if (DEBUG) Slog.d(TAG, "Started intent=" + mTargetIntent);
}
// Mark the target activity as launch-behind to bump its visibility for the
@@ -383,6 +433,21 @@
}
}
+ private void startRecentsActivityInBackground(String reason) {
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchActivityType(mTargetActivityType);
+ options.setAvoidMoveToFront();
+ mTargetIntent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NO_ANIMATION);
+
+ mActivityStartController
+ .obtainStarter(mTargetIntent, reason)
+ .setCallingUid(mRecentsUid)
+ .setCallingPackage(mRecentsComponent.getPackageName())
+ .setActivityOptions(new SafeActivityOptions(options))
+ .setMayWait(mUserId)
+ .execute();
+ }
+
/**
* Called only when the animation should be canceled prior to starting.
*/
@@ -412,15 +477,15 @@
* @return the top activity in the {@param targetStack} matching the {@param component}, or just
* the top activity of the top task if no task matches the component.
*/
- private ActivityRecord getTargetActivity(ActivityStack targetStack, ComponentName component,
- int userId) {
+ private ActivityRecord getTargetActivity(ActivityStack targetStack) {
if (targetStack == null) {
return null;
}
for (int i = targetStack.getChildCount() - 1; i >= 0; i--) {
final TaskRecord task = targetStack.getChildAt(i);
- if (task.userId == userId && task.getBaseIntent().getComponent().equals(component)) {
+ if (task.userId == mUserId
+ && task.getBaseIntent().getComponent().equals(mTargetIntent.getComponent())) {
return task.getTopActivity();
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index 4986a6d..ecf3acd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -85,6 +85,7 @@
import java.io.File;
import java.util.List;
+import java.util.function.Consumer;
/**
* A base class to handle common operations in activity related unit tests.
@@ -169,8 +170,12 @@
* Delegates task creation to {@link #TaskBuilder} to avoid the dependency of window hierarchy
* when starting activity in unit tests.
*/
- void mockTaskRecordFactory() {
- final TaskRecord task = new TaskBuilder(mSupervisor).setCreateStack(false).build();
+ void mockTaskRecordFactory(Consumer<TaskBuilder> taskBuilderSetup) {
+ final TaskBuilder taskBuilder = new TaskBuilder(mSupervisor).setCreateStack(false);
+ if (taskBuilderSetup != null) {
+ taskBuilderSetup.accept(taskBuilder);
+ }
+ final TaskRecord task = taskBuilder.build();
final TaskRecordFactory factory = mock(TaskRecordFactory.class);
TaskRecord.setTaskRecordFactory(factory);
doReturn(task).when(factory).create(any() /* service */, anyInt() /* taskId */,
@@ -178,6 +183,10 @@
any() /* voiceInteractor */);
}
+ void mockTaskRecordFactory() {
+ mockTaskRecordFactory(null /* taskBuilderSetup */);
+ }
+
/**
* Builder for creating new activities.
*/
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index 1f8b33e..9630b7d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -42,8 +42,11 @@
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import android.app.IApplicationThread;
import android.content.ComponentName;
import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.platform.test.annotations.Presubmit;
import android.view.IRecentsAnimationRunner;
@@ -111,6 +114,57 @@
}
@Test
+ public void testPreloadRecentsActivity() {
+ // Ensure that the fake recent component can be resolved by the recents intent.
+ mockTaskRecordFactory(builder -> builder.setComponent(mRecentsComponent));
+ ActivityInfo aInfo = new ActivityInfo();
+ aInfo.applicationInfo = new ApplicationInfo();
+ aInfo.applicationInfo.uid = 10001;
+ aInfo.applicationInfo.targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
+ aInfo.packageName = aInfo.applicationInfo.packageName = mRecentsComponent.getPackageName();
+ aInfo.processName = "recents";
+ doReturn(aInfo).when(mSupervisor).resolveActivity(any() /* intent */, any() /* rInfo */,
+ anyInt() /* startFlags */, any() /* profilerInfo */);
+
+ // Assume its process is alive because the caller should be the recents service.
+ WindowProcessController wpc = new WindowProcessController(mService, aInfo.applicationInfo,
+ aInfo.processName, aInfo.applicationInfo.uid, 0 /* userId */,
+ mock(Object.class) /* owner */, mock(WindowProcessListener.class));
+ wpc.setThread(mock(IApplicationThread.class));
+ doReturn(wpc).when(mService).getProcessController(eq(wpc.mName), eq(wpc.mUid));
+
+ Intent recentsIntent = new Intent().setComponent(mRecentsComponent);
+ // Null animation indicates to preload.
+ mService.startRecentsActivity(recentsIntent, null /* assistDataReceiver */,
+ null /* recentsAnimationRunner */);
+
+ ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
+ ActivityStack recentsStack = display.getStack(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_RECENTS);
+ assertThat(recentsStack).isNotNull();
+
+ ActivityRecord recentsActivity = recentsStack.getTopActivity();
+ // The activity is started in background so it should be invisible and will be stopped.
+ assertThat(recentsActivity).isNotNull();
+ assertThat(mSupervisor.mStoppingActivities).contains(recentsActivity);
+ assertFalse(recentsActivity.visible);
+
+ // Assume it is stopped to test next use case.
+ recentsActivity.activityStoppedLocked(null /* newIcicle */, null /* newPersistentState */,
+ null /* description */);
+ mSupervisor.mStoppingActivities.remove(recentsActivity);
+
+ spyOn(recentsActivity);
+ // Start when the recents activity exists. It should ensure the configuration.
+ mService.startRecentsActivity(recentsIntent, null /* assistDataReceiver */,
+ null /* recentsAnimationRunner */);
+
+ verify(recentsActivity).ensureActivityConfiguration(anyInt() /* globalChanges */,
+ anyBoolean() /* preserveWindow */, eq(true) /* ignoreVisibility */);
+ assertThat(mSupervisor.mStoppingActivities).contains(recentsActivity);
+ }
+
+ @Test
public void testRestartRecentsActivity() throws Exception {
// Have a recents activity that is not attached to its process (ActivityRecord.app = null).
ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 4a9b174..484fd3b 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -2121,27 +2121,24 @@
* @hide
*/
@UnsupportedAppUsage
- public static @NonNull int[] getActiveSubscriptionIdList() {
- return getActiveSubscriptionIdList(true);
- }
+ public @NonNull int[] getActiveSubscriptionIdList() {
+ int[] subId = null;
- /**
- * @return a non-null list of subId's that are active.
- *
- * @hide
- */
- public static @NonNull int[] getActiveSubscriptionIdList(boolean visibleOnly) {
try {
ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
if (iSub != null) {
- int[] subId = iSub.getActiveSubIdList(visibleOnly);
- if (subId != null) return subId;
+ subId = iSub.getActiveSubIdList(/*visibleOnly*/true);
}
} catch (RemoteException ex) {
// ignore it
}
- return new int[0];
+ if (subId == null) {
+ subId = new int[0];
+ }
+
+ return subId;
+
}
/**
diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
index 5b57c9d..7a0ab9c 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
@@ -605,11 +605,15 @@
*/
private static boolean checkCarrierPrivilegeForAnySubId(Context context,
Supplier<ITelephony> telephonySupplier, int uid) {
- int[] activeSubIds = SubscriptionManager.getActiveSubscriptionIdList(/*visibleOnly*/ false);
- for (int activeSubId : activeSubIds) {
- if (getCarrierPrivilegeStatus(telephonySupplier, activeSubId, uid)
- == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
- return true;
+ SubscriptionManager sm = (SubscriptionManager) context.getSystemService(
+ Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+ int[] activeSubIds = sm.getActiveSubscriptionIdList();
+ if (activeSubIds != null) {
+ for (int activeSubId : activeSubIds) {
+ if (getCarrierPrivilegeStatus(telephonySupplier, activeSubId, uid)
+ == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+ return true;
+ }
}
}
return false;