summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/test-current.txt5
-rw-r--r--api/test-lint-baseline.txt10
-rw-r--r--core/java/android/hardware/display/DisplayManager.java6
-rw-r--r--core/java/android/provider/Settings.java5
-rw-r--r--core/jni/android_media_AudioSystem.cpp11
-rw-r--r--media/java/android/media/MediaRouter.java97
-rw-r--r--media/jni/audioeffect/android_media_AudioEffect.cpp2
-rw-r--r--media/jni/soundpool/android_media_SoundPool.cpp2
-rw-r--r--media/tests/MediaRouter/Android.bp3
-rw-r--r--media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouteInfoTest.java195
-rw-r--r--services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java53
-rw-r--r--services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java4
-rw-r--r--tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java81
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);
}
}