diff options
13 files changed, 405 insertions, 69 deletions
diff --git a/api/test-current.txt b/api/test-current.txt index 76663378caf1..0bdc6c6901a9 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -3487,6 +3487,11 @@ package android.provider { public static final class Settings.Secure extends android.provider.Settings.NameValueTable { method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static void resetToDefaults(@NonNull android.content.ContentResolver, @Nullable String); field public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED = "accessibility_display_magnification_enabled"; + field public static final String ACCESSIBILITY_MAGNIFICATION_CAPABILITY = "accessibility_magnification_capability"; + field public static final String ACCESSIBILITY_MAGNIFICATION_MODE = "accessibility_magnification_mode"; + field public static final int ACCESSIBILITY_MAGNIFICATION_MODE_ALL = 3; // 0x3 + field public static final int ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN = 1; // 0x1 + field public static final int ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW = 2; // 0x2 field public static final String ACCESSIBILITY_SHORTCUT_TARGET_SERVICE = "accessibility_shortcut_target_service"; field public static final String ANR_SHOW_BACKGROUND = "anr_show_background"; field public static final String AUTOFILL_FEATURE_FIELD_CLASSIFICATION = "autofill_field_classification"; diff --git a/api/test-lint-baseline.txt b/api/test-lint-baseline.txt index 91a09e3d8890..a314702b7792 100644 --- a/api/test-lint-baseline.txt +++ b/api/test-lint-baseline.txt @@ -2530,6 +2530,16 @@ NoSettingsProvider: android.provider.Settings.Global#TETHER_OFFLOAD_DISABLED: NoSettingsProvider: android.provider.Settings.Global#USE_OPEN_WIFI_PACKAGE: NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED: + +NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_CAPABILITY: + +NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE: + +NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_ALL: + +NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN: + +NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW: NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_SHORTCUT_TARGET_SERVICE: diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index dda1890b0757..fc14b89d83f7 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -16,6 +16,8 @@ package android.hardware.display; +import static android.view.Display.DEFAULT_DISPLAY; + import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; @@ -381,6 +383,7 @@ public final class DisplayManager { addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_EXTERNAL); addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_OVERLAY); addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_VIRTUAL); + addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_INTERNAL); } return mTempDisplays.toArray(new Display[mTempDisplays.size()]); } finally { @@ -401,6 +404,9 @@ public final class DisplayManager { private void addPresentationDisplaysLocked( ArrayList<Display> displays, int[] displayIds, int matchType) { for (int i = 0; i < displayIds.length; i++) { + if (displayIds[i] == DEFAULT_DISPLAY) { + continue; + } Display display = getOrCreateDisplayLocked(displayIds[i], true /*assumeValid*/); if (display != null && (display.getFlags() & Display.FLAG_PRESENTATION) != 0 diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 7df9a5fc7a51..8ad76692db04 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -9088,6 +9088,7 @@ public final class Settings { * @see#ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW * @hide */ + @TestApi public static final String ACCESSIBILITY_MAGNIFICATION_MODE = "accessibility_magnification_mode"; @@ -9095,12 +9096,14 @@ public final class Settings { * Magnification mode value that magnifies whole display. * @hide */ + @TestApi public static final int ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN = 0x1; /** * Magnification mode value that magnifies magnify particular region in a window * @hide */ + @TestApi public static final int ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW = 0x2; /** @@ -9108,6 +9111,7 @@ public final class Settings { * region in a window. * @hide */ + @TestApi public static final int ACCESSIBILITY_MAGNIFICATION_MODE_ALL = 0x3; /** @@ -9119,6 +9123,7 @@ public final class Settings { * @see#ACCESSIBILITY_MAGNIFICATION_MODE_ALL * @hide */ + @TestApi public static final String ACCESSIBILITY_MAGNIFICATION_CAPABILITY = "accessibility_magnification_capability"; diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index 5c4c5099bf4c..1ca45fe9f70b 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -338,7 +338,7 @@ static jint getVectorOfAudioDeviceTypeAddr(JNIEnv *env, jintArray deviceTypes, return (jint)AUDIO_JAVA_BAD_VALUE; } const char *address = env->GetStringUTFChars((jstring)addrJobj, NULL); - AudioDeviceTypeAddr dev = AudioDeviceTypeAddr(typesPtr[i], address); + AudioDeviceTypeAddr dev = AudioDeviceTypeAddr((audio_devices_t)typesPtr[i], address); audioDeviceTypeAddrVector.push_back(dev); env->ReleaseStringUTFChars((jstring)addrJobj, address); } @@ -820,7 +820,8 @@ static void convertAudioGainConfigToNative(JNIEnv *env, bool useInMask) { nAudioGainConfig->index = env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mIndex); - nAudioGainConfig->mode = env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mMode); + nAudioGainConfig->mode = + (audio_gain_mode_t)env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mMode); ALOGV("convertAudioGainConfigToNative got gain index %d", nAudioGainConfig->index); jint jMask = env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mChannelMask); audio_channel_mask_t nMask; @@ -940,8 +941,8 @@ static jint convertAudioPortConfigToNativeWithDevicePort(JNIEnv *env, jobject jAudioDevicePort = env->GetObjectField(jAudioPortConfig, gAudioPortConfigFields.mPort); - nAudioPortConfig->ext.device.type = env->GetIntField(jAudioDevicePort, - gAudioPortFields.mType); + nAudioPortConfig->ext.device.type = + (audio_devices_t)env->GetIntField(jAudioDevicePort, gAudioPortFields.mType); jstring jDeviceAddress = (jstring)env->GetObjectField(jAudioDevicePort, gAudioPortFields.mAddress); const char *nDeviceAddress = env->GetStringUTFChars(jDeviceAddress, NULL); @@ -2334,7 +2335,7 @@ static jint android_media_AudioSystem_setSupportedSystemUsages(JNIEnv *env, jobj static jint android_media_AudioSystem_setAllowedCapturePolicy(JNIEnv *env, jobject thiz, jint uid, jint flags) { - return AudioSystem::setAllowedCapturePolicy(uid, flags); + return AudioSystem::setAllowedCapturePolicy(uid, static_cast<audio_flags_mask_t>(flags)); } static jint diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java index 2b3f420cd834..4d87fb3fa81f 100644 --- a/media/java/android/media/MediaRouter.java +++ b/media/java/android/media/MediaRouter.java @@ -45,6 +45,9 @@ import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; import android.view.Display; +import android.view.DisplayAddress; + +import com.android.internal.annotations.VisibleForTesting; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -1737,7 +1740,9 @@ public class MediaRouter { */ private static final int DEFAULT_PLAYBACK_VOLUME = DEFAULT_PLAYBACK_MAX_VOLUME; - RouteInfo(RouteCategory category) { + /** @hide */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + public RouteInfo(RouteCategory category) { mCategory = category; mDeviceType = DEVICE_TYPE_UNKNOWN; } @@ -2078,7 +2083,9 @@ public class MediaRouter { return mPresentationDisplay; } - boolean updatePresentationDisplay() { + /** @hide */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + public boolean updatePresentationDisplay() { Display display = choosePresentationDisplay(); if (mPresentationDisplay != display) { mPresentationDisplay = display; @@ -2088,41 +2095,81 @@ public class MediaRouter { } private Display choosePresentationDisplay() { - if ((mSupportedTypes & ROUTE_TYPE_LIVE_VIDEO) != 0) { - Display[] displays = sStatic.getAllPresentationDisplays(); - - // Ensure that the specified display is valid for presentations. - // This check will normally disallow the default display unless it was - // configured as a presentation display for some reason. - if (mPresentationDisplayId >= 0) { - for (Display display : displays) { - if (display.getDisplayId() == mPresentationDisplayId) { - return display; - } + if ((getSupportedTypes() & ROUTE_TYPE_LIVE_VIDEO) == 0) { + return null; + } + final Display[] displays = getAllPresentationDisplays(); + if (displays == null || displays.length == 0) { + return null; + } + + // Ensure that the specified display is valid for presentations. + // This check will normally disallow the default display unless it was + // configured as a presentation display for some reason. + if (mPresentationDisplayId >= 0) { + for (Display display : displays) { + if (display.getDisplayId() == mPresentationDisplayId) { + return display; } - return null; } + return null; + } - // Find the indicated Wifi display by its address. - if (mDeviceAddress != null) { - for (Display display : displays) { - if (display.getType() == Display.TYPE_WIFI - && mDeviceAddress.equals(display.getAddress())) { - return display; - } + // Find the indicated Wifi display by its address. + if (getDeviceAddress() != null) { + for (Display display : displays) { + if (display.getType() == Display.TYPE_WIFI + && displayAddressEquals(display)) { + return display; } - return null; } + } + + // Returns the first hard-wired display. + for (Display display : displays) { + if (display.getType() == Display.TYPE_EXTERNAL) { + return display; + } + } - // For the default route, choose the first presentation display from the list. - if (this == sStatic.mDefaultAudioVideo && displays.length > 0) { - return displays[0]; + // Returns the first non-default built-in display. + for (Display display : displays) { + if (display.getType() == Display.TYPE_INTERNAL) { + return display; } } + + // For the default route, choose the first presentation display from the list. + if (this == getDefaultAudioVideo()) { + return displays[0]; + } return null; } /** @hide */ + @VisibleForTesting + public Display[] getAllPresentationDisplays() { + return sStatic.getAllPresentationDisplays(); + } + + /** @hide */ + @VisibleForTesting + public RouteInfo getDefaultAudioVideo() { + return sStatic.mDefaultAudioVideo; + } + + private boolean displayAddressEquals(Display display) { + final DisplayAddress displayAddress = display.getAddress(); + // mDeviceAddress recorded mac address. If displayAddress is not a kind of Network, + // return false early. + if (!(displayAddress instanceof DisplayAddress.Network)) { + return false; + } + final DisplayAddress.Network networkAddress = (DisplayAddress.Network) displayAddress; + return getDeviceAddress().equals(networkAddress.toString()); + } + + /** @hide */ @UnsupportedAppUsage public String getDeviceAddress() { return mDeviceAddress; diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp index 45c49e58f390..0d53ab152129 100644 --- a/media/jni/audioeffect/android_media_AudioEffect.cpp +++ b/media/jni/audioeffect/android_media_AudioEffect.cpp @@ -331,7 +331,7 @@ android_media_AudioEffect_native_setup(JNIEnv *env, jobject thiz, jobject weak_t } if (deviceType != AUDIO_DEVICE_NONE) { - device.mType = deviceType; + device.mType = (audio_devices_t)deviceType; ScopedUtfChars address(env, deviceAddress); device.setAddress(address.c_str()); } diff --git a/media/jni/soundpool/android_media_SoundPool.cpp b/media/jni/soundpool/android_media_SoundPool.cpp index ca3cc8552990..26725f87bfdc 100644 --- a/media/jni/soundpool/android_media_SoundPool.cpp +++ b/media/jni/soundpool/android_media_SoundPool.cpp @@ -200,7 +200,7 @@ android_media_SoundPool_native_setup(JNIEnv *env, jobject thiz, jobject weakRef, paa->usage = (audio_usage_t) env->GetIntField(jaa, javaAudioAttrFields.fieldUsage); paa->content_type = (audio_content_type_t) env->GetIntField(jaa, javaAudioAttrFields.fieldContentType); - paa->flags = env->GetIntField(jaa, javaAudioAttrFields.fieldFlags); + paa->flags = (audio_flags_mask_t) env->GetIntField(jaa, javaAudioAttrFields.fieldFlags); ALOGV("android_media_SoundPool_native_setup"); auto *ap = new SoundPool(maxChannels, paa); diff --git a/media/tests/MediaRouter/Android.bp b/media/tests/MediaRouter/Android.bp index 5a0a50c2ae38..4d0c258843f9 100644 --- a/media/tests/MediaRouter/Android.bp +++ b/media/tests/MediaRouter/Android.bp @@ -11,7 +11,8 @@ android_test { static_libs: [ "android-support-test", "mockito-target-minus-junit4", - "testng" + "testng", + "truth-prebuilt", ], platform_apis: true, diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouteInfoTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouteInfoTest.java new file mode 100644 index 000000000000..92e4c9554cb4 --- /dev/null +++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouteInfoTest.java @@ -0,0 +1,195 @@ +/* + * 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.mediaroutertest; + +import static com.google.common.truth.Truth.assertThat; + +import android.hardware.display.DisplayManagerGlobal; +import android.media.MediaRouter; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import android.view.Display; +import android.view.DisplayAddress; +import android.view.DisplayAdjustments; +import android.view.DisplayInfo; + +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class MediaRouteInfoTest { + private TestableRouteInfo mRouteInfo; + private static Display sWifiDisplay; + private static Display sExternalDisplay; + private static Display sInternalDisplay; + private static final String FAKE_MAC_ADDRESS = "fake MAC address"; + + @BeforeClass + public static void setUpOnce() { + final DisplayManagerGlobal global = DisplayManagerGlobal.getInstance(); + final DisplayInfo wifiInfo = new DisplayInfo(); + wifiInfo.flags = Display.FLAG_PRESENTATION; + wifiInfo.type = Display.TYPE_WIFI; + wifiInfo.address = DisplayAddress.fromMacAddress(FAKE_MAC_ADDRESS); + sWifiDisplay = new Display(global, 2, wifiInfo, new DisplayAdjustments()); + + final DisplayInfo externalInfo = new DisplayInfo(); + externalInfo.flags = Display.FLAG_PRESENTATION; + externalInfo.type = Display.TYPE_EXTERNAL; + sExternalDisplay = new Display(global, 3, externalInfo, new DisplayAdjustments()); + + final DisplayInfo internalInfo = new DisplayInfo(); + internalInfo.flags = Display.FLAG_PRESENTATION; + internalInfo.type = Display.TYPE_INTERNAL; + sInternalDisplay = new Display(global, 4, internalInfo, new DisplayAdjustments()); + } + + @Before + public void setUp() { + mRouteInfo = new TestableRouteInfo(); + } + + @Test + public void testGetPresentationDisplay_notLiveVideo() { + mRouteInfo.setPresentationDisplays(sWifiDisplay); + mRouteInfo.mSupportedType = MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY; + + mRouteInfo.updatePresentationDisplay(); + + assertThat(mRouteInfo.getPresentationDisplay()).isNull(); + } + + @Test + public void testGetPresentationDisplay_includesLiveVideo() { + mRouteInfo.setPresentationDisplays(sWifiDisplay); + mRouteInfo.mSupportedType |= MediaRouter.ROUTE_TYPE_LIVE_AUDIO; + + mRouteInfo.updatePresentationDisplay(); + + assertThat(mRouteInfo.getPresentationDisplay()).isEqualTo(sWifiDisplay); + } + + @Test + public void testGetPresentationDisplay_noPresentationDisplay() { + mRouteInfo.updatePresentationDisplay(); + + assertThat(mRouteInfo.getPresentationDisplay()).isNull(); + } + + @Test + public void testGetPresentationDisplay_wifiDisplayOnly() { + mRouteInfo.setPresentationDisplays(sWifiDisplay); + + mRouteInfo.updatePresentationDisplay(); + + assertThat(mRouteInfo.getPresentationDisplay()).isEqualTo(sWifiDisplay); + } + + @Test + public void testGetPresentationDisplay_externalDisplayOnly() { + mRouteInfo.setPresentationDisplays(sExternalDisplay); + + mRouteInfo.updatePresentationDisplay(); + + assertThat(mRouteInfo.getPresentationDisplay()).isEqualTo(sExternalDisplay); + } + + @Test + public void testGetPresentationDisplay_internalDisplayOnly() { + mRouteInfo.setPresentationDisplays(sInternalDisplay); + + mRouteInfo.updatePresentationDisplay(); + + assertThat(mRouteInfo.getPresentationDisplay()).isEqualTo(sInternalDisplay); + } + + @Test + public void testGetPresentationDisplay_addressNotMatch() { + mRouteInfo.setPresentationDisplays(sWifiDisplay); + mRouteInfo.mDeviceAddress = "Not match"; + + mRouteInfo.updatePresentationDisplay(); + + assertThat(mRouteInfo.getPresentationDisplay()).isNull(); + } + + @Test + public void testGetPresentationDisplay_containsWifiAndExternalDisplays_returnWifiDisplay() { + mRouteInfo.setPresentationDisplays(sExternalDisplay, sWifiDisplay); + + mRouteInfo.updatePresentationDisplay(); + + assertThat(mRouteInfo.getPresentationDisplay()).isEqualTo(sWifiDisplay); + } + + @Test + public void testGetPresentationDisplay_containsExternalAndInternalDisplays_returnExternal() { + mRouteInfo.setPresentationDisplays(sExternalDisplay, sInternalDisplay); + + mRouteInfo.updatePresentationDisplay(); + + assertThat(mRouteInfo.getPresentationDisplay()).isEqualTo(sExternalDisplay); + } + + @Test + public void testGetPresentationDisplay_containsAllDisplays_returnWifiDisplay() { + mRouteInfo.setPresentationDisplays(sExternalDisplay, sInternalDisplay, sWifiDisplay); + + mRouteInfo.updatePresentationDisplay(); + + assertThat(mRouteInfo.getPresentationDisplay()).isEqualTo(sWifiDisplay); + } + + private static class TestableRouteInfo extends MediaRouter.RouteInfo { + private Display[] mDisplays = new Display[0]; + private int mSupportedType = MediaRouter.ROUTE_TYPE_LIVE_VIDEO; + private String mDeviceAddress = FAKE_MAC_ADDRESS; + private MediaRouter.RouteInfo mDefaultRouteInfo = null; + + private TestableRouteInfo() { + super(null); + } + + private void setPresentationDisplays(Display ...displays) { + mDisplays = new Display[displays.length]; + System.arraycopy(displays, 0, mDisplays, 0, displays.length); + } + + @Override + public Display[] getAllPresentationDisplays() { + return mDisplays; + } + + @Override + public int getSupportedTypes() { + return mSupportedType; + } + + @Override + public String getDeviceAddress() { + return mDeviceAddress; + } + + @Override + public MediaRouter.RouteInfo getDefaultAudioVideo() { + return mDefaultRouteInfo; + } + } +} diff --git a/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java b/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java index d202a2a60738..5646c752fc90 100644 --- a/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java +++ b/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java @@ -30,6 +30,7 @@ import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; +import android.util.Pair; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.CollectionUtils; @@ -94,39 +95,41 @@ public class NetworkStatsSubscriptionsMonitor extends // also needed to track CBRS. final List<Integer> newSubs = getActiveSubIdList(mSubscriptionManager); - for (final int subId : newSubs) { - final RatTypeListener match = CollectionUtils.find(mRatListeners, - it -> it.mSubId == subId); - if (match != null) continue; - - // Create listener for every newly added sub. Also store subscriberId into it to - // prevent binder call to telephony when querying RAT. If the subscriberId is empty - // for any reason, such as SIM PIN locked, skip registration. - // SubscriberId will be unavailable again if 1. modem crashed 2. reboot - // 3. re-insert SIM. If that happens, the listeners will be eventually synchronized - // with active sub list once all subscriberIds are ready. - final String subscriberId = mTeleManager.getSubscriberId(subId); - if (TextUtils.isEmpty(subscriberId)) { - Log.d(NetworkStatsService.TAG, "Empty subscriberId for newly added sub " - + subId + ", skip listener registration"); + // IMSI is needed for every newly added sub. Listener stores subscriberId into it to + // prevent binder call to telephony when querying RAT. Keep listener registration with empty + // IMSI is meaningless since the RAT type changed is ambiguous for multi-SIM if reported + // with empty IMSI. So filter the subs w/o a valid IMSI to prevent such registration. + final List<Pair<Integer, String>> filteredNewSubs = + CollectionUtils.mapNotNull(newSubs, subId -> { + final String subscriberId = mTeleManager.getSubscriberId(subId); + return TextUtils.isEmpty(subscriberId) ? null : new Pair(subId, subscriberId); + }); + + for (final Pair<Integer, String> sub : filteredNewSubs) { + // Fully match listener with subId and IMSI, since in some rare cases, IMSI might be + // suddenly change regardless of subId, such as switch IMSI feature in modem side. + // If that happens, register new listener with new IMSI and remove old one later. + if (CollectionUtils.find(mRatListeners, + it -> it.equalsKey(sub.first, sub.second)) != null) { continue; } + final RatTypeListener listener = - new RatTypeListener(mExecutor, this, subId, subscriberId); + new RatTypeListener(mExecutor, this, sub.first, sub.second); mRatListeners.add(listener); // Register listener to the telephony manager that associated with specific sub. - mTeleManager.createForSubscriptionId(subId) + mTeleManager.createForSubscriptionId(sub.first) .listen(listener, PhoneStateListener.LISTEN_SERVICE_STATE); - Log.d(NetworkStatsService.TAG, "RAT type listener registered for sub " + subId); + Log.d(NetworkStatsService.TAG, "RAT type listener registered for sub " + sub.first); } for (final RatTypeListener listener : new ArrayList<>(mRatListeners)) { - // If the new list contains the subId of the listener, keeps it. - final Integer match = CollectionUtils.find(newSubs, it -> it == listener.mSubId); - if (match != null) continue; - - handleRemoveRatTypeListener(listener); + // If there is no subId and IMSI matched the listener, removes it. + if (CollectionUtils.find(filteredNewSubs, + it -> listener.equalsKey(it.first, it.second)) == null) { + handleRemoveRatTypeListener(listener); + } } } @@ -232,5 +235,9 @@ public class NetworkStatsSubscriptionsMonitor extends public int getSubId() { return mSubId; } + + boolean equalsKey(int subId, @NonNull String subscriberId) { + return mSubId == subId && TextUtils.equals(mSubscriberId, subscriberId); + } } } diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java index 964dbecf705c..c4c862e7e629 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java @@ -199,10 +199,10 @@ public final class TimeZoneDetectorCallbackImpl implements TimeZoneDetectorStrat } private boolean isGeoDetectionEnabled(@UserIdInt int userId) { - final boolean locationEnabled = isLocationEnabled(userId); + final boolean geoDetectionEnabledByDefault = false; return Settings.Secure.getIntForUser(mCr, Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED, - locationEnabled ? 1 : 0 /* defaultValue */, userId) != 0; + (geoDetectionEnabledByDefault ? 1 : 0) /* defaultValue */, userId) != 0; } private void setGeoDetectionEnabled(@UserIdInt int userId, boolean enabled) { diff --git a/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java b/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java index 8f093779da11..6d2c7dc39ffd 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java @@ -21,6 +21,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -150,7 +151,7 @@ public final class NetworkStatsSubscriptionsMonitorTest { } private void assertRatTypeChangedForSub(String subscriberId, int ratType) { - assertEquals(mMonitor.getRatTypeForSubscriberId(subscriberId), ratType); + assertEquals(ratType, mMonitor.getRatTypeForSubscriberId(subscriberId)); final ArgumentCaptor<Integer> typeCaptor = ArgumentCaptor.forClass(Integer.class); // Verify callback with the subscriberId and the RAT type should be as expected. // It will fail if get a callback with an unexpected RAT type. @@ -302,26 +303,84 @@ public final class NetworkStatsSubscriptionsMonitorTest { reset(mDelegate); // Set IMSI to null again to simulate somehow IMSI is not available, such as - // modem crash. Verify service should not unregister listener. + // modem crash. Verify service should unregister listener. updateSubscriberIdForTestSub(TEST_SUBID1, null); - verify(mTelephonyManager, never()).listen(any(), anyInt()); - assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS); + verify(mTelephonyManager, times(1)).listen(eq(ratTypeListenerCaptor.getValue()), + eq(PhoneStateListener.LISTEN_NONE)); + assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN); reset(mDelegate); + clearInvocations(mTelephonyManager); - // Set RAT type of sim1 to LTE. Verify RAT type of sim1 is still changed even if the IMSI - // is not available. The monitor keeps the listener even if the IMSI disappears because - // the IMSI can never change for any given subId, therefore even if the IMSI is updated - // to null, the monitor should continue accepting updates of the RAT type. However, - // telephony is never actually supposed to do this, if the IMSI disappears there should - // not be updates, but it's still the right thing to do theoretically. - setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID1, + // Simulate somehow IMSI is back. Verify service will register with + // another listener and fire callback accordingly. + final ArgumentCaptor<RatTypeListener> ratTypeListenerCaptor2 = + ArgumentCaptor.forClass(RatTypeListener.class); + updateSubscriberIdForTestSub(TEST_SUBID1, TEST_IMSI1); + verify(mTelephonyManager, times(1)).listen(ratTypeListenerCaptor2.capture(), + eq(PhoneStateListener.LISTEN_SERVICE_STATE)); + assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN); + reset(mDelegate); + clearInvocations(mTelephonyManager); + + // Set RAT type of sim1 to LTE. Verify RAT type of sim1 still works. + setRatTypeForSub(ratTypeListenerCaptor2.getAllValues(), TEST_SUBID1, TelephonyManager.NETWORK_TYPE_LTE); assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_LTE); reset(mDelegate); mMonitor.stop(); + verify(mTelephonyManager, times(1)).listen(eq(ratTypeListenerCaptor2.getValue()), + eq(PhoneStateListener.LISTEN_NONE)); + assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN); + } + + /** + * Verify that when IMSI suddenly changed for a given subId, the service will register a new + * listener and unregister the old one, and report changes on updated IMSI. This is for modem + * feature that may be enabled for certain carrier, which changes to use a different IMSI while + * roaming on certain networks for multi-IMSI SIM cards, but the subId stays the same. + */ + @Test + public void testSubscriberIdChanged() { + mMonitor.start(); + // Insert sim1, verify RAT type is NETWORK_TYPE_UNKNOWN, and never get any callback + // before changing RAT type. + addTestSub(TEST_SUBID1, TEST_IMSI1); + final ArgumentCaptor<RatTypeListener> ratTypeListenerCaptor = + ArgumentCaptor.forClass(RatTypeListener.class); + verify(mTelephonyManager, times(1)).listen(ratTypeListenerCaptor.capture(), + eq(PhoneStateListener.LISTEN_SERVICE_STATE)); + assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN); + + // Set RAT type of sim1 to UMTS. + // Verify RAT type of sim1 changes accordingly. + setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID1, + TelephonyManager.NETWORK_TYPE_UMTS); + assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS); + reset(mDelegate); + clearInvocations(mTelephonyManager); + + // Simulate IMSI of sim1 changed to IMSI2. Verify the service will register with + // another listener and remove the old one. The RAT type of new IMSI stays at + // NETWORK_TYPE_UNKNOWN until received initial callback from telephony. + final ArgumentCaptor<RatTypeListener> ratTypeListenerCaptor2 = + ArgumentCaptor.forClass(RatTypeListener.class); + updateSubscriberIdForTestSub(TEST_SUBID1, TEST_IMSI2); + verify(mTelephonyManager, times(1)).listen(ratTypeListenerCaptor2.capture(), + eq(PhoneStateListener.LISTEN_SERVICE_STATE)); verify(mTelephonyManager, times(1)).listen(eq(ratTypeListenerCaptor.getValue()), eq(PhoneStateListener.LISTEN_NONE)); assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN); + assertRatTypeNotChangedForSub(TEST_IMSI2, TelephonyManager.NETWORK_TYPE_UNKNOWN); + reset(mDelegate); + + // Set RAT type of sim1 to UMTS for new listener to simulate the initial callback received + // from telephony after registration. Verify RAT type of sim1 changes with IMSI2 + // accordingly. + setRatTypeForSub(ratTypeListenerCaptor2.getAllValues(), TEST_SUBID1, + TelephonyManager.NETWORK_TYPE_UMTS); + assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN); + assertRatTypeChangedForSub(TEST_IMSI2, TelephonyManager.NETWORK_TYPE_UMTS); + reset(mDelegate); } } |