diff options
42 files changed, 1078 insertions, 192 deletions
diff --git a/TEST_MAPPING b/TEST_MAPPING index a48ce0ce1b6c..0ba24d112b63 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -73,14 +73,6 @@ ] }, { - "name": "TestablesTests", - "options": [ - { - "exclude-annotation": "androidx.test.filters.FlakyTest" - } - ] - }, - { "name": "FrameworksCoreTests", "options": [ { diff --git a/core/api/current.txt b/core/api/current.txt index b243374878fc..4c1594b19409 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -18727,8 +18727,8 @@ package android.hardware.usb { method public int bulkTransfer(android.hardware.usb.UsbEndpoint, byte[], int, int, int); method public boolean claimInterface(android.hardware.usb.UsbInterface, boolean); method public void close(); - method public int controlTransfer(int, int, int, int, byte[], int, int); - method public int controlTransfer(int, int, int, int, byte[], int, int, int); + method public int controlTransfer(int, int, int, int, @Nullable byte[], int, int); + method public int controlTransfer(int, int, int, int, @Nullable byte[], int, int, int); method public int getFileDescriptor(); method public byte[] getRawDescriptors(); method public String getSerial(); @@ -41362,6 +41362,8 @@ package android.telephony { field public static final String KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY = "carrier_nr_availabilities_int_array"; field public static final String KEY_CARRIER_PROVISIONS_WIFI_MERGED_NETWORKS_BOOL = "carrier_provisions_wifi_merged_networks_bool"; field public static final String KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL = "carrier_rcs_provisioning_required_bool"; + field public static final String KEY_CARRIER_SERVICE_NAME_STRING_ARRAY = "carrier_service_name_array"; + field public static final String KEY_CARRIER_SERVICE_NUMBER_STRING_ARRAY = "carrier_service_number_array"; field public static final String KEY_CARRIER_SETTINGS_ACTIVITY_COMPONENT_NAME_STRING = "carrier_settings_activity_component_name_string"; field public static final String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool"; field public static final String KEY_CARRIER_SUPPORTS_OPP_DATA_AUTO_PROVISIONING_BOOL = "carrier_supports_opp_data_auto_provisioning_bool"; @@ -42743,9 +42745,12 @@ package android.telephony { method public int getDomain(); method @Nullable public String getRegisteredPlmn(); method public int getTransportType(); - method public boolean isRegistered(); - method public boolean isRoaming(); - method public boolean isSearching(); + method public boolean isNetworkRegistered(); + method public boolean isNetworkRoaming(); + method public boolean isNetworkSearching(); + method @Deprecated public boolean isRegistered(); + method @Deprecated public boolean isRoaming(); + method @Deprecated public boolean isSearching(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.telephony.NetworkRegistrationInfo> CREATOR; field public static final int DOMAIN_CS = 1; // 0x1 @@ -43499,6 +43504,7 @@ package android.telephony { method public int describeContents(); method public int getNetworkType(); method public int getOverrideNetworkType(); + method public boolean isRoaming(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.telephony.TelephonyDisplayInfo> CREATOR; field public static final int OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO = 2; // 0x2 diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 398b596e2621..821944f406cd 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -12828,7 +12828,8 @@ package android.telephony { public final class NetworkRegistrationInfo implements android.os.Parcelable { method @Nullable public android.telephony.DataSpecificRegistrationInfo getDataSpecificInfo(); - method public int getRegistrationState(); + method public int getNetworkRegistrationState(); + method @Deprecated public int getRegistrationState(); method public int getRejectCause(); method public int getRoamingType(); method public boolean isEmergencyEnabled(); diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 7cfb66a3dcc6..6a5ef3a67488 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -141,8 +141,9 @@ package android.app { field public static final int PROCESS_CAPABILITY_FOREGROUND_CAMERA = 2; // 0x2 field public static final int PROCESS_CAPABILITY_FOREGROUND_LOCATION = 1; // 0x1 field public static final int PROCESS_CAPABILITY_FOREGROUND_MICROPHONE = 4; // 0x4 - field public static final int PROCESS_CAPABILITY_NETWORK = 8; // 0x8 + field @Deprecated public static final int PROCESS_CAPABILITY_NETWORK = 8; // 0x8 field public static final int PROCESS_CAPABILITY_NONE = 0; // 0x0 + field public static final int PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK = 8; // 0x8 field public static final int PROCESS_STATE_FOREGROUND_SERVICE = 4; // 0x4 field public static final int PROCESS_STATE_TOP = 2; // 0x2 field public static final int STOP_USER_ON_SWITCH_DEFAULT = -1; // 0xffffffff diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index eadf758e2b99..d328b34a2a8f 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -643,9 +643,17 @@ public class ActivityManager { @TestApi public static final int PROCESS_CAPABILITY_FOREGROUND_MICROPHONE = 1 << 2; - /** @hide Process can access network despite any power saving resrictions */ + /** @hide Process can access network despite any power saving restrictions */ @TestApi - public static final int PROCESS_CAPABILITY_NETWORK = 1 << 3; + public static final int PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK = 1 << 3; + /** + * @hide + * @deprecated Use {@link #PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK} instead. + */ + @TestApi + @Deprecated + public static final int PROCESS_CAPABILITY_NETWORK = + PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK; /** @hide all capabilities, the ORing of all flags in {@link ProcessCapability}*/ @TestApi diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 559989368a9f..5ba7a4cb4f54 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -238,6 +238,7 @@ import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.lang.reflect.Method; import java.net.InetAddress; +import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; @@ -4221,18 +4222,20 @@ public final class ActivityThread extends ClientTransactionHandler static void handleAttachStartupAgents(String dataDir) { try { - Path code_cache = ContextImpl.getCodeCacheDirBeforeBind(new File(dataDir)).toPath(); - if (!Files.exists(code_cache)) { + Path codeCache = ContextImpl.getCodeCacheDirBeforeBind(new File(dataDir)).toPath(); + if (!Files.exists(codeCache)) { return; } - Path startup_path = code_cache.resolve("startup_agents"); - if (Files.exists(startup_path)) { - for (Path p : Files.newDirectoryStream(startup_path)) { - handleAttachAgent( - p.toAbsolutePath().toString() - + "=" - + dataDir, - null); + Path startupPath = codeCache.resolve("startup_agents"); + if (Files.exists(startupPath)) { + try (DirectoryStream<Path> startupFiles = Files.newDirectoryStream(startupPath)) { + for (Path p : startupFiles) { + handleAttachAgent( + p.toAbsolutePath().toString() + + "=" + + dataDir, + null); + } } } } catch (Exception e) { diff --git a/core/java/android/hardware/usb/UsbDeviceConnection.java b/core/java/android/hardware/usb/UsbDeviceConnection.java index 7c2e518b8544..44144d92f56a 100644 --- a/core/java/android/hardware/usb/UsbDeviceConnection.java +++ b/core/java/android/hardware/usb/UsbDeviceConnection.java @@ -238,7 +238,7 @@ public class UsbDeviceConnection { * or negative value for failure */ public int controlTransfer(int requestType, int request, int value, - int index, byte[] buffer, int length, int timeout) { + int index, @Nullable byte[] buffer, int length, int timeout) { return controlTransfer(requestType, request, value, index, buffer, 0, length, timeout); } @@ -263,7 +263,7 @@ public class UsbDeviceConnection { * or negative value for failure */ public int controlTransfer(int requestType, int request, int value, int index, - byte[] buffer, int offset, int length, int timeout) { + @Nullable byte[] buffer, int offset, int length, int timeout) { checkBounds(buffer, offset, length); return native_control_request(requestType, request, value, index, buffer, offset, length, timeout); diff --git a/core/java/android/net/Ikev2VpnProfile.java b/core/java/android/net/Ikev2VpnProfile.java index a20191cab774..74775a820620 100644 --- a/core/java/android/net/Ikev2VpnProfile.java +++ b/core/java/android/net/Ikev2VpnProfile.java @@ -769,6 +769,19 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { } } + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("IkeV2VpnProfile ["); + sb.append(" MaxMtu=" + mMaxMtu); + if (mIsBypassable) sb.append(" Bypassable"); + if (mRequiresInternetValidation) sb.append(" RequiresInternetValidation"); + if (mIsRestrictedToTestNetworks) sb.append(" RestrictedToTestNetworks"); + if (mAutomaticNattKeepaliveTimerEnabled) sb.append(" AutomaticNattKeepaliveTimerEnabled"); + if (mAutomaticIpVersionSelectionEnabled) sb.append(" AutomaticIpVersionSelectionEnabled"); + sb.append("]"); + return sb.toString(); + } + /** A incremental builder for IKEv2 VPN profiles */ public static final class Builder { private int mType = -1; diff --git a/core/java/android/os/RemoteException.java b/core/java/android/os/RemoteException.java index 878e1413a8c9..970f4192c36c 100644 --- a/core/java/android/os/RemoteException.java +++ b/core/java/android/os/RemoteException.java @@ -21,6 +21,12 @@ import android.util.AndroidException; /** * Parent exception for all Binder remote-invocation errors + * + * Note: not all exceptions from binder services will be subclasses of this. + * For instance, RuntimeException and several subclasses of it may be + * thrown as well as OutOfMemoryException. + * + * One common subclass is {@link DeadObjectException}. */ public class RemoteException extends AndroidException { public RemoteException() { diff --git a/core/java/android/view/OWNERS b/core/java/android/view/OWNERS index 0c881567c6da..a3c278f487fc 100644 --- a/core/java/android/view/OWNERS +++ b/core/java/android/view/OWNERS @@ -60,6 +60,9 @@ per-file SurfaceView.java = file:/services/core/java/com/android/server/wm/OWNER per-file SurfaceHolder.java = file:/graphics/java/android/graphics/OWNERS per-file SurfaceHolder.java = file:/services/core/java/com/android/server/wm/OWNERS +# Text +per-file HandwritingInitiator.java = file:/core/java/android/text/OWNERS + # View per-file View.java = file:/services/accessibility/OWNERS per-file View.java = file:/core/java/android/service/autofill/OWNERS diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index a5e96176ec4c..277bdf388188 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1334,8 +1334,7 @@ android:permissionFlags="hardRestricted" android:protectionLevel="dangerous" /> - <!-- Allows an application to write (but not read) the user's - call log data. + <!-- Allows an application to write and read the user's call log data. <p class="note"><strong>Note:</strong> If your app uses the {@link #WRITE_CONTACTS} permission and <em>both</em> your <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code diff --git a/keystore/java/android/security/KeyStore2.java b/keystore/java/android/security/KeyStore2.java index c2cd6ffc622c..74597c5cd874 100644 --- a/keystore/java/android/security/KeyStore2.java +++ b/keystore/java/android/security/KeyStore2.java @@ -161,6 +161,15 @@ public class KeyStore2 { } /** + * List all entries in the keystore for in the given namespace. + */ + public KeyDescriptor[] listBatch(int domain, long namespace, String startPastAlias) + throws KeyStoreException { + return handleRemoteExceptionWithRetry( + (service) -> service.listEntriesBatched(domain, namespace, startPastAlias)); + } + + /** * Grant string prefix as used by the keystore boringssl engine. Must be kept in sync * with system/security/keystore-engine. Note: The prefix here includes the 0x which * std::stringstream used in keystore-engine needs to identify the number as hex represented. @@ -301,6 +310,13 @@ public class KeyStore2 { }); } + /** + * Returns the number of Keystore entries for a given domain and namespace. + */ + public int getNumberOfEntries(int domain, long namespace) throws KeyStoreException { + return handleRemoteExceptionWithRetry((service) + -> service.getNumberOfEntries(domain, namespace)); + } protected static void interruptedPreservingSleep(long millis) { boolean wasInterrupted = false; Calendar calendar = Calendar.getInstance(); diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java index 91f216f1320a..045e318ff513 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java @@ -79,13 +79,11 @@ import java.security.spec.NamedParameterSpec; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.Date; import java.util.Enumeration; -import java.util.HashSet; import java.util.Iterator; import java.util.List; -import java.util.Set; +import java.util.NoSuchElementException; import javax.crypto.SecretKey; @@ -1043,26 +1041,22 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi { } } - private Set<String> getUniqueAliases() { + private KeyDescriptor[] getAliasesBatch(String startPastAlias) { try { - final KeyDescriptor[] keys = mKeyStore.list( + return mKeyStore.listBatch( getTargetDomain(), - mNamespace + mNamespace, + startPastAlias ); - final Set<String> aliases = new HashSet<>(keys.length); - for (KeyDescriptor d : keys) { - aliases.add(d.alias); - } - return aliases; } catch (android.security.KeyStoreException e) { Log.e(TAG, "Failed to list keystore entries.", e); - return new HashSet<>(); + return new KeyDescriptor[0]; } } @Override public Enumeration<String> engineAliases() { - return Collections.enumeration(getUniqueAliases()); + return new KeyEntriesEnumerator(); } @Override @@ -1073,12 +1067,18 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi { return getKeyMetadata(alias) != null; } - @Override public int engineSize() { - return getUniqueAliases().size(); + try { + return mKeyStore.getNumberOfEntries( + getTargetDomain(), + mNamespace + ); + } catch (android.security.KeyStoreException e) { + Log.e(TAG, "Failed to get the number of keystore entries.", e); + return 0; + } } - @Override public boolean engineIsKeyEntry(String alias) { return isKeyEntry(alias); @@ -1251,4 +1251,38 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi { + "or TrustedCertificateEntry; was " + entry); } } + + private class KeyEntriesEnumerator implements Enumeration<String> { + private KeyDescriptor[] mCurrentBatch; + private int mCurrentEntry = 0; + private String mLastAlias = null; + private KeyEntriesEnumerator() { + getAndValidateNextBatch(); + } + + private void getAndValidateNextBatch() { + mCurrentBatch = getAliasesBatch(mLastAlias); + mCurrentEntry = 0; + } + + public boolean hasMoreElements() { + return (mCurrentBatch != null) && (mCurrentBatch.length > 0); + } + + public String nextElement() { + if ((mCurrentBatch == null) || (mCurrentBatch.length == 0)) { + throw new NoSuchElementException("Error while fetching entries."); + } + final KeyDescriptor currentEntry = mCurrentBatch[mCurrentEntry]; + mLastAlias = currentEntry.alias; + + mCurrentEntry++; + // This was the last entry in the batch. + if (mCurrentEntry >= mCurrentBatch.length) { + getAndValidateNextBatch(); + } + + return mLastAlias; + } + } } diff --git a/keystore/tests/src/android/security/keystore2/AndroidKeyStoreSpiTest.java b/keystore/tests/src/android/security/keystore2/AndroidKeyStoreSpiTest.java index f96c39c879fd..1e1f68aaefc1 100644 --- a/keystore/tests/src/android/security/keystore2/AndroidKeyStoreSpiTest.java +++ b/keystore/tests/src/android/security/keystore2/AndroidKeyStoreSpiTest.java @@ -17,9 +17,14 @@ package android.security.keystore2; import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertThrows; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.anyLong; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.isNull; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import android.security.KeyStore2; @@ -36,6 +41,12 @@ import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; +import java.util.NoSuchElementException; + public class AndroidKeyStoreSpiTest { @Mock @@ -48,14 +59,176 @@ public class AndroidKeyStoreSpiTest { @Test public void testEngineAliasesReturnsEmptySetOnKeyStoreError() throws Exception { - when(mKeystore2.list(anyInt(), anyLong())) + when(mKeystore2.listBatch(anyInt(), anyLong(), isNull())) .thenThrow(new KeyStoreException(6, "Some Error")); AndroidKeyStoreSpi spi = new AndroidKeyStoreSpi(); spi.initForTesting(mKeystore2); assertThat("Empty collection expected", !spi.engineAliases().hasMoreElements()); - verify(mKeystore2).list(anyInt(), anyLong()); + verify(mKeystore2).listBatch(anyInt(), anyLong(), isNull()); + } + + @Test + public void testEngineAliasesCorrectlyListsZeroEntriesEmptyArray() throws Exception { + when(mKeystore2.listBatch(anyInt(), anyLong(), anyString())) + .thenReturn(new KeyDescriptor[0]); + AndroidKeyStoreSpi spi = new AndroidKeyStoreSpi(); + spi.initForTesting(mKeystore2); + + Enumeration<String> aliases = spi.engineAliases(); + assertThat("Should not have any elements", !aliases.hasMoreElements()); + assertThrows("Should have no elements to return", NoSuchElementException.class, + () -> aliases.nextElement()); + verify(mKeystore2).listBatch(anyInt(), anyLong(), isNull()); + } + + @Test + public void testEngineAliasesCorrectlyListsZeroEntriesNullArray() throws Exception { + when(mKeystore2.listBatch(anyInt(), anyLong(), anyString())) + .thenReturn(null); + AndroidKeyStoreSpi spi = new AndroidKeyStoreSpi(); + spi.initForTesting(mKeystore2); + + Enumeration<String> aliases = spi.engineAliases(); + assertThat("Should not have any elements", !aliases.hasMoreElements()); + assertThrows("Should have no elements to return", NoSuchElementException.class, + () -> aliases.nextElement()); + verify(mKeystore2).listBatch(anyInt(), anyLong(), isNull()); + } + + private static KeyDescriptor newKeyDescriptor(String alias) { + KeyDescriptor result = new KeyDescriptor(); + result.alias = alias; + return result; + } + + private static KeyDescriptor[] createKeyDescriptorsArray(int numEntries) { + KeyDescriptor[] kds = new KeyDescriptor[numEntries]; + for (int i = 0; i < kds.length; i++) { + kds[i] = newKeyDescriptor(String.format("alias-%d", i)); + } + + return kds; + } + + private static void assertAliasListsEqual( + KeyDescriptor[] keyDescriptors, Enumeration<String> aliasesEnumerator) { + List<String> aliases = Collections.list(aliasesEnumerator); + Assert.assertArrayEquals(Arrays.stream(keyDescriptors).map(kd -> kd.alias).toArray(), + aliases.toArray()); + } + + @Test + public void testEngineAliasesCorrectlyListsEntriesInASingleBatch() throws Exception { + final String alias1 = "testAlias1"; + final String alias2 = "testAlias2"; + final String alias3 = "testAlias3"; + KeyDescriptor[] kds = {newKeyDescriptor(alias1), + newKeyDescriptor(alias2), newKeyDescriptor(alias3)}; + when(mKeystore2.listBatch(anyInt(), anyLong(), eq(null))) + .thenReturn(kds); + when(mKeystore2.listBatch(anyInt(), anyLong(), eq("testAlias3"))) + .thenReturn(null); + + AndroidKeyStoreSpi spi = new AndroidKeyStoreSpi(); + spi.initForTesting(mKeystore2); + + Enumeration<String> aliases = spi.engineAliases(); + assertThat("Should have more elements before first.", aliases.hasMoreElements()); + Assert.assertEquals(aliases.nextElement(), alias1); + assertThat("Should have more elements before second.", aliases.hasMoreElements()); + Assert.assertEquals(aliases.nextElement(), alias2); + assertThat("Should have more elements before third.", aliases.hasMoreElements()); + Assert.assertEquals(aliases.nextElement(), alias3); + assertThat("Should have no more elements after third.", !aliases.hasMoreElements()); + verify(mKeystore2).listBatch(anyInt(), anyLong(), eq(null)); + verify(mKeystore2).listBatch(anyInt(), anyLong(), eq("testAlias3")); + verifyNoMoreInteractions(mKeystore2); + } + + @Test + public void testEngineAliasesCorrectlyListsEntriesInMultipleBatches() throws Exception { + final int numEntries = 2500; + KeyDescriptor[] kds = createKeyDescriptorsArray(numEntries); + when(mKeystore2.listBatch(anyInt(), anyLong(), eq(null))) + .thenReturn(Arrays.copyOfRange(kds, 0, 1000)); + when(mKeystore2.listBatch(anyInt(), anyLong(), eq("alias-999"))) + .thenReturn(Arrays.copyOfRange(kds, 1000, 2000)); + when(mKeystore2.listBatch(anyInt(), anyLong(), eq("alias-1999"))) + .thenReturn(Arrays.copyOfRange(kds, 2000, 2500)); + when(mKeystore2.listBatch(anyInt(), anyLong(), eq("alias-2499"))) + .thenReturn(null); + + AndroidKeyStoreSpi spi = new AndroidKeyStoreSpi(); + spi.initForTesting(mKeystore2); + + assertAliasListsEqual(kds, spi.engineAliases()); + verify(mKeystore2).listBatch(anyInt(), anyLong(), eq(null)); + verify(mKeystore2).listBatch(anyInt(), anyLong(), eq("alias-999")); + verify(mKeystore2).listBatch(anyInt(), anyLong(), eq("alias-1999")); + verify(mKeystore2).listBatch(anyInt(), anyLong(), eq("alias-2499")); + verifyNoMoreInteractions(mKeystore2); + } + + @Test + public void testEngineAliasesCorrectlyListsEntriesWhenNumEntriesIsExactlyOneBatchSize() + throws Exception { + final int numEntries = 1000; + KeyDescriptor[] kds = createKeyDescriptorsArray(numEntries); + when(mKeystore2.listBatch(anyInt(), anyLong(), eq(null))) + .thenReturn(kds); + when(mKeystore2.listBatch(anyInt(), anyLong(), eq("alias-999"))) + .thenReturn(null); + + AndroidKeyStoreSpi spi = new AndroidKeyStoreSpi(); + spi.initForTesting(mKeystore2); + + assertAliasListsEqual(kds, spi.engineAliases()); + verify(mKeystore2).listBatch(anyInt(), anyLong(), eq(null)); + verify(mKeystore2).listBatch(anyInt(), anyLong(), eq("alias-999")); + verifyNoMoreInteractions(mKeystore2); + } + + @Test + public void testEngineAliasesCorrectlyListsEntriesWhenNumEntriesIsAMultiplyOfBatchSize() + throws Exception { + final int numEntries = 2000; + KeyDescriptor[] kds = createKeyDescriptorsArray(numEntries); + when(mKeystore2.listBatch(anyInt(), anyLong(), eq(null))) + .thenReturn(Arrays.copyOfRange(kds, 0, 1000)); + when(mKeystore2.listBatch(anyInt(), anyLong(), eq("alias-999"))) + .thenReturn(Arrays.copyOfRange(kds, 1000, 2000)); + when(mKeystore2.listBatch(anyInt(), anyLong(), eq("alias-1999"))) + .thenReturn(null); + + AndroidKeyStoreSpi spi = new AndroidKeyStoreSpi(); + spi.initForTesting(mKeystore2); + + assertAliasListsEqual(kds, spi.engineAliases()); + verify(mKeystore2).listBatch(anyInt(), anyLong(), eq(null)); + verify(mKeystore2).listBatch(anyInt(), anyLong(), eq("alias-999")); + verify(mKeystore2).listBatch(anyInt(), anyLong(), eq("alias-1999")); + verifyNoMoreInteractions(mKeystore2); + } + + @Test + public void testEngineAliasesCorrectlyListsEntriesWhenReturningLessThanBatchSize() + throws Exception { + final int numEntries = 500; + KeyDescriptor[] kds = createKeyDescriptorsArray(numEntries); + when(mKeystore2.listBatch(anyInt(), anyLong(), eq(null))) + .thenReturn(kds); + when(mKeystore2.listBatch(anyInt(), anyLong(), eq("alias-499"))) + .thenReturn(new KeyDescriptor[0]); + + AndroidKeyStoreSpi spi = new AndroidKeyStoreSpi(); + spi.initForTesting(mKeystore2); + + assertAliasListsEqual(kds, spi.engineAliases()); + verify(mKeystore2).listBatch(anyInt(), anyLong(), eq(null)); + verify(mKeystore2).listBatch(anyInt(), anyLong(), eq("alias-499")); + verifyNoMoreInteractions(mKeystore2); } @Mock diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp index 5a67eb9935dd..738f1ab0c18f 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -98,7 +98,7 @@ bool Properties::load() { debugOverdraw = false; std::string debugOverdrawProperty = base::GetProperty(PROPERTY_DEBUG_OVERDRAW, ""); if (debugOverdrawProperty != "") { - INIT_LOGD(" Overdraw debug enabled: %s", debugOverdrawProperty); + INIT_LOGD(" Overdraw debug enabled: %s", debugOverdrawProperty.c_str()); if (debugOverdrawProperty == "show") { debugOverdraw = true; overdrawColorSet = OverdrawColorSet::Default; diff --git a/packages/CtsShim/Android.bp b/packages/CtsShim/Android.bp index 31cd76079131..baafe7ba570c 100644 --- a/packages/CtsShim/Android.bp +++ b/packages/CtsShim/Android.bp @@ -44,6 +44,9 @@ android_app_import { arm64: { apk: "apk/arm/CtsShimPriv.apk", }, + riscv64: { + apk: "apk/riscv64/CtsShimPriv.apk", + }, x86: { apk: "apk/x86/CtsShimPriv.apk", }, @@ -82,6 +85,9 @@ android_app_import { arm64: { apk: "apk/arm/CtsShim.apk", }, + riscv64: { + apk: "apk/riscv64/CtsShim.apk", + }, x86: { apk: "apk/x86/CtsShim.apk", }, diff --git a/packages/PackageInstaller/AndroidManifest.xml b/packages/PackageInstaller/AndroidManifest.xml index 6669d6ba3085..868867de7b42 100644 --- a/packages/PackageInstaller/AndroidManifest.xml +++ b/packages/PackageInstaller/AndroidManifest.xml @@ -1,5 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" package="com.android.packageinstaller"> <original-package android:name="com.android.packageinstaller" /> @@ -142,6 +143,9 @@ android:authorities="com.google.android.packageinstaller.wear.provider" android:grantUriPermissions="true" android:exported="true" /> + + <receiver android:name="androidx.profileinstaller.ProfileInstallReceiver" + tools:node="remove" /> </application> </manifest> diff --git a/packages/SettingsLib/search/processor-src/com/android/settingslib/search/IndexableProcessor.java b/packages/SettingsLib/search/processor-src/com/android/settingslib/search/IndexableProcessor.java index f305fd35b7d9..e92157e7c867 100644 --- a/packages/SettingsLib/search/processor-src/com/android/settingslib/search/IndexableProcessor.java +++ b/packages/SettingsLib/search/processor-src/com/android/settingslib/search/IndexableProcessor.java @@ -47,7 +47,7 @@ import javax.tools.Diagnostic.Kind; * Annotation processor for {@link SearchIndexable} that generates {@link SearchIndexableResources} * subclasses. */ -@SupportedSourceVersion(SourceVersion.RELEASE_11) +@SupportedSourceVersion(SourceVersion.RELEASE_17) @SupportedAnnotationTypes({"com.android.settingslib.search.SearchIndexable"}) public class IndexableProcessor extends AbstractProcessor { diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java index 39b4b8e16708..35e3dd3379f0 100644 --- a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java +++ b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java @@ -231,7 +231,7 @@ public class MobileStatusTracker { public SignalStrength signalStrength; public TelephonyDisplayInfo telephonyDisplayInfo = new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_UNKNOWN, - TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE); + TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false); /** * Empty constructor diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/OWNERS b/packages/SettingsLib/src/com/android/settingslib/mobile/OWNERS index ab9b5dc3ff7a..c01528fb4f18 100644 --- a/packages/SettingsLib/src/com/android/settingslib/mobile/OWNERS +++ b/packages/SettingsLib/src/com/android/settingslib/mobile/OWNERS @@ -1,4 +1,7 @@ # Default reviewers for this and subdirectories. -bonianchen@google.com +songferngwang@google.com +zoeychen@google.com # Emergency approvers in case the above are not available +changbetty@google.com +tomhsu@google.com diff --git a/packages/SettingsLib/src/com/android/settingslib/net/OWNERS b/packages/SettingsLib/src/com/android/settingslib/net/OWNERS index ab9b5dc3ff7a..c01528fb4f18 100644 --- a/packages/SettingsLib/src/com/android/settingslib/net/OWNERS +++ b/packages/SettingsLib/src/com/android/settingslib/net/OWNERS @@ -1,4 +1,7 @@ # Default reviewers for this and subdirectories. -bonianchen@google.com +songferngwang@google.com +zoeychen@google.com # Emergency approvers in case the above are not available +changbetty@google.com +tomhsu@google.com diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java index 2e6ea0e28d86..54946ee4e662 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java @@ -144,7 +144,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final TelephonyDisplayInfo DEFAULT_TELEPHONY_DISPLAY_INFO = new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_UNKNOWN, - TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE); + TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false); static final int MAX_WIFI_ENTRY_COUNT = 3; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileState.kt index 1fb6a982fdf3..c37b01fff578 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileState.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileState.kt @@ -49,7 +49,7 @@ internal class MobileState( ) : ConnectivityState() { @JvmField var telephonyDisplayInfo = TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_UNKNOWN, - TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE) + TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false) @JvmField var serviceState: ServiceState? = null @JvmField var signalStrength: SignalStrength? = null @@ -131,7 +131,7 @@ internal class MobileState( } fun isRoaming(): Boolean { - return serviceState != null && serviceState!!.roaming + return telephonyDisplayInfo != null && telephonyDisplayInfo.isRoaming } /** diff --git a/services/core/java/com/android/server/MmsServiceBroker.java b/services/core/java/com/android/server/MmsServiceBroker.java index 59db68691223..f149fda7a043 100644 --- a/services/core/java/com/android/server/MmsServiceBroker.java +++ b/services/core/java/com/android/server/MmsServiceBroker.java @@ -45,6 +45,7 @@ import android.telephony.TelephonyManager; import android.util.Slog; import com.android.internal.telephony.IMms; +import com.android.internal.telephony.TelephonyPermissions; import com.android.server.uri.NeededUriGrants; import com.android.server.uri.UriGrantsManagerInternal; @@ -337,6 +338,14 @@ public class MmsServiceBroker extends SystemService { throws RemoteException { Slog.d(TAG, "sendMessage() by " + callingPkg); mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, "Send MMS message"); + + // Check if user is associated with the subscription + if (!TelephonyPermissions.checkSubscriptionAssociatedWithUser(mContext, subId, + Binder.getCallingUserHandle())) { + // TODO(b/258629881): Display error dialog. + return; + } + if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), callingPkg, attributionTag, null) != AppOpsManager.MODE_ALLOWED) { Slog.e(TAG, callingPkg + " is not allowed to call sendMessage()"); diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index a69d3f0276f3..77a54a568859 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -1957,7 +1957,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { && overrideNetworkType == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED) { overrideNetworkType = TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE; } - return new TelephonyDisplayInfo(networkType, overrideNetworkType); + boolean isRoaming = telephonyDisplayInfo.isRoaming(); + return new TelephonyDisplayInfo(networkType, overrideNetworkType, isRoaming); } public void notifyCallForwardingChanged(boolean cfi) { diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java index f652cb050cbd..78d4708e70a2 100644 --- a/services/core/java/com/android/server/VcnManagementService.java +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -1104,7 +1104,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { final NetworkCapabilities result = ncBuilder.build(); final VcnUnderlyingNetworkPolicy policy = new VcnUnderlyingNetworkPolicy( mTrackingNetworkCallback - .requiresRestartForImmutableCapabilityChanges(result), + .requiresRestartForImmutableCapabilityChanges(result, linkProperties), result); logVdbg("getUnderlyingNetworkPolicy() called for caps: " + networkCapabilities @@ -1354,19 +1354,29 @@ public class VcnManagementService extends IVcnManagementService.Stub { * without requiring a Network restart. */ private class TrackingNetworkCallback extends ConnectivityManager.NetworkCallback { + private final Object mLockObject = new Object(); private final Map<Network, NetworkCapabilities> mCaps = new ArrayMap<>(); + private final Map<Network, LinkProperties> mLinkProperties = new ArrayMap<>(); @Override public void onCapabilitiesChanged(Network network, NetworkCapabilities caps) { - synchronized (mCaps) { + synchronized (mLockObject) { mCaps.put(network, caps); } } @Override + public void onLinkPropertiesChanged(Network network, LinkProperties lp) { + synchronized (mLockObject) { + mLinkProperties.put(network, lp); + } + } + + @Override public void onLost(Network network) { - synchronized (mCaps) { + synchronized (mLockObject) { mCaps.remove(network); + mLinkProperties.remove(network); } } @@ -1393,22 +1403,28 @@ public class VcnManagementService extends IVcnManagementService.Stub { return true; } - private boolean requiresRestartForImmutableCapabilityChanges(NetworkCapabilities caps) { + private boolean requiresRestartForImmutableCapabilityChanges( + NetworkCapabilities caps, LinkProperties lp) { if (caps.getSubscriptionIds() == null) { return false; } - synchronized (mCaps) { - for (NetworkCapabilities existing : mCaps.values()) { - if (caps.getSubscriptionIds().equals(existing.getSubscriptionIds()) - && hasSameTransportsAndCapabilities(caps, existing)) { - // Restart if any immutable capabilities have changed - return existing.hasCapability(NET_CAPABILITY_NOT_RESTRICTED) + synchronized (mLockObject) { + // Search for an existing network (using interfce names) + // TODO: Get network from NetworkFactory (if exists) for this match. + for (Entry<Network, LinkProperties> lpEntry : mLinkProperties.entrySet()) { + if (lp.getInterfaceName() != null + && !lp.getInterfaceName().isEmpty() + && Objects.equals( + lp.getInterfaceName(), lpEntry.getValue().getInterfaceName())) { + return mCaps.get(lpEntry.getKey()) + .hasCapability(NET_CAPABILITY_NOT_RESTRICTED) != caps.hasCapability(NET_CAPABILITY_NOT_RESTRICTED); } } } + // If no network found, by definition does not need restart. return false; } diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index abddc4366460..256df98ae760 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -2573,7 +2573,10 @@ public final class ProcessList { + ", " + reason); app.setPendingStart(false); killProcessQuiet(pid); - Process.killProcessGroup(app.uid, app.getPid()); + final int appPid = app.getPid(); + if (appPid != 0) { + Process.killProcessGroup(app.uid, appPid); + } noteAppKill(app, ApplicationExitInfo.REASON_OTHER, ApplicationExitInfo.SUBREASON_INVALID_START, reason); return false; diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 5ca03b8e6f8b..46ab3304c46e 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -1014,9 +1014,14 @@ public class AudioService extends IAudioService.Stub mSfxHelper = new SoundEffectsHelper(mContext, playerBase -> ignorePlayerLogs(playerBase)); - final boolean headTrackingDefault = mContext.getResources().getBoolean( + final boolean binauralEnabledDefault = SystemProperties.getBoolean( + "ro.audio.spatializer_binaural_enabled_default", true); + final boolean transauralEnabledDefault = SystemProperties.getBoolean( + "ro.audio.spatializer_transaural_enabled_default", true); + final boolean headTrackingEnabledDefault = mContext.getResources().getBoolean( com.android.internal.R.bool.config_spatial_audio_head_tracking_enabled_default); - mSpatializerHelper = new SpatializerHelper(this, mAudioSystem, headTrackingDefault); + mSpatializerHelper = new SpatializerHelper(this, mAudioSystem, + binauralEnabledDefault, transauralEnabledDefault, headTrackingEnabledDefault); mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); mHasVibrator = mVibrator == null ? false : mVibrator.hasVibrator(); diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java index 2b566668a7c7..c9cdba970ccb 100644 --- a/services/core/java/com/android/server/audio/SpatializerHelper.java +++ b/services/core/java/com/android/server/audio/SpatializerHelper.java @@ -106,12 +106,12 @@ public class SpatializerHelper { }; // Spatializer state machine - private static final int STATE_UNINITIALIZED = 0; - private static final int STATE_NOT_SUPPORTED = 1; - private static final int STATE_DISABLED_UNAVAILABLE = 3; - private static final int STATE_ENABLED_UNAVAILABLE = 4; - private static final int STATE_ENABLED_AVAILABLE = 5; - private static final int STATE_DISABLED_AVAILABLE = 6; + /*package*/ static final int STATE_UNINITIALIZED = 0; + /*package*/ static final int STATE_NOT_SUPPORTED = 1; + /*package*/ static final int STATE_DISABLED_UNAVAILABLE = 3; + /*package*/ static final int STATE_ENABLED_UNAVAILABLE = 4; + /*package*/ static final int STATE_ENABLED_AVAILABLE = 5; + /*package*/ static final int STATE_DISABLED_AVAILABLE = 6; private int mState = STATE_UNINITIALIZED; private boolean mFeatureEnabled = false; @@ -147,9 +147,9 @@ public class SpatializerHelper { .setSampleRate(48000) .setChannelMask(AudioFormat.CHANNEL_OUT_5POINT1) .build(); - // device array to store the routing for the default attributes and format, size 1 because - // media is never expected to be duplicated - private static final AudioDeviceAttributes[] ROUTING_DEVICES = new AudioDeviceAttributes[1]; + // device array to store the routing for the default attributes and format, initialized to + // an empty list as routing hasn't been established yet + private static ArrayList<AudioDeviceAttributes> sRoutingDevices = new ArrayList<>(0); //--------------------------------------------------------------- // audio device compatibility / enabled @@ -171,18 +171,17 @@ public class SpatializerHelper { // initialization @SuppressWarnings("StaticAssignmentInConstructor") SpatializerHelper(@NonNull AudioService mother, @NonNull AudioSystemAdapter asa, - boolean headTrackingEnabledByDefault) { + boolean binauralEnabledDefault, + boolean transauralEnabledDefault, + boolean headTrackingEnabledDefault) { mAudioService = mother; mASA = asa; // "StaticAssignmentInConstructor" warning is suppressed as the SpatializerHelper being // constructed here is the factory for SADeviceState, thus SADeviceState and its // private static field sHeadTrackingEnabledDefault should never be accessed directly. - SADeviceState.sHeadTrackingEnabledDefault = headTrackingEnabledByDefault; - } - - synchronized void initForTest(boolean hasBinaural, boolean hasTransaural) { - mBinauralSupported = hasBinaural; - mTransauralSupported = hasTransaural; + SADeviceState.sBinauralEnabledDefault = binauralEnabledDefault; + SADeviceState.sTransauralEnabledDefault = transauralEnabledDefault; + SADeviceState.sHeadTrackingEnabledDefault = headTrackingEnabledDefault; } synchronized void init(boolean effectExpected, @Nullable String settings) { @@ -318,8 +317,7 @@ public class SpatializerHelper { return; } mState = STATE_DISABLED_UNAVAILABLE; - mASA.getDevicesForAttributes( - DEFAULT_ATTRIBUTES, false /* forVolume */).toArray(ROUTING_DEVICES); + sRoutingDevices = getRoutingDevices(DEFAULT_ATTRIBUTES); // note at this point mSpat is still not instantiated } @@ -361,34 +359,35 @@ public class SpatializerHelper { case STATE_DISABLED_AVAILABLE: break; } - mASA.getDevicesForAttributes( - DEFAULT_ATTRIBUTES, false /* forVolume */).toArray(ROUTING_DEVICES); + + sRoutingDevices = getRoutingDevices(DEFAULT_ATTRIBUTES); // check validity of routing information - if (ROUTING_DEVICES[0] == null) { - logloge("onRoutingUpdated: device is null, no Spatial Audio"); + if (sRoutingDevices.isEmpty()) { + logloge("onRoutingUpdated: no device, no Spatial Audio"); setDispatchAvailableState(false); // not changing the spatializer level as this is likely a transient state return; } + final AudioDeviceAttributes currentDevice = sRoutingDevices.get(0); // is media routed to a new device? - if (isWireless(ROUTING_DEVICES[0].getType())) { - addWirelessDeviceIfNew(ROUTING_DEVICES[0]); + if (isWireless(currentDevice.getType())) { + addWirelessDeviceIfNew(currentDevice); } // find if media device enabled / available - final Pair<Boolean, Boolean> enabledAvailable = evaluateState(ROUTING_DEVICES[0]); + final Pair<Boolean, Boolean> enabledAvailable = evaluateState(currentDevice); boolean able = false; if (enabledAvailable.second) { // available for Spatial audio, check w/ effect - able = canBeSpatializedOnDevice(DEFAULT_ATTRIBUTES, DEFAULT_FORMAT, ROUTING_DEVICES); + able = canBeSpatializedOnDevice(DEFAULT_ATTRIBUTES, DEFAULT_FORMAT, sRoutingDevices); loglogi("onRoutingUpdated: can spatialize media 5.1:" + able - + " on device:" + ROUTING_DEVICES[0]); + + " on device:" + currentDevice); setDispatchAvailableState(able); } else { - loglogi("onRoutingUpdated: device:" + ROUTING_DEVICES[0] + loglogi("onRoutingUpdated: device:" + currentDevice + " not available for Spatial Audio"); setDispatchAvailableState(false); } @@ -396,10 +395,10 @@ public class SpatializerHelper { boolean enabled = able && enabledAvailable.first; if (enabled) { loglogi("Enabling Spatial Audio since enabled for media device:" - + ROUTING_DEVICES[0]); + + currentDevice); } else { loglogi("Disabling Spatial Audio since disabled for media device:" - + ROUTING_DEVICES[0]); + + currentDevice); } if (mSpat != null) { byte level = enabled ? (byte) Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL @@ -732,9 +731,13 @@ public class SpatializerHelper { } private synchronized boolean canBeSpatializedOnDevice(@NonNull AudioAttributes attributes, - @NonNull AudioFormat format, @NonNull AudioDeviceAttributes[] devices) { - if (isDeviceCompatibleWithSpatializationModes(devices[0])) { - return AudioSystem.canBeSpatialized(attributes, format, devices); + @NonNull AudioFormat format, @NonNull ArrayList<AudioDeviceAttributes> devices) { + if (devices.isEmpty()) { + return false; + } + if (isDeviceCompatibleWithSpatializationModes(devices.get(0))) { + AudioDeviceAttributes[] devArray = new AudioDeviceAttributes[devices.size()]; + return AudioSystem.canBeSpatialized(attributes, format, devices.toArray(devArray)); } return false; } @@ -1010,10 +1013,13 @@ public class SpatializerHelper { logd("canBeSpatialized false due to usage:" + attributes.getUsage()); return false; } - AudioDeviceAttributes[] devices = new AudioDeviceAttributes[1]; + // going through adapter to take advantage of routing cache - mASA.getDevicesForAttributes( - attributes, false /* forVolume */).toArray(devices); + final ArrayList<AudioDeviceAttributes> devices = getRoutingDevices(attributes); + if (devices.isEmpty()) { + logloge("canBeSpatialized got no device for " + attributes); + return false; + } final boolean able = canBeSpatializedOnDevice(attributes, format, devices); logd("canBeSpatialized usage:" + attributes.getUsage() + " format:" + format.toLogFriendlyString() + " returning " + able); @@ -1144,8 +1150,13 @@ public class SpatializerHelper { logDeviceState(deviceState, "setHeadTrackerEnabled"); // check current routing to see if it affects the headtracking mode - if (ROUTING_DEVICES[0] != null && ROUTING_DEVICES[0].getType() == ada.getType() - && ROUTING_DEVICES[0].getAddress().equals(ada.getAddress())) { + if (sRoutingDevices.isEmpty()) { + logloge("setHeadTrackerEnabled: no device, bailing"); + return; + } + final AudioDeviceAttributes currentDevice = sRoutingDevices.get(0); + if (currentDevice.getType() == ada.getType() + && currentDevice.getAddress().equals(ada.getAddress())) { setDesiredHeadTrackingMode(enabled ? mDesiredHeadTrackingModeWhenEnabled : Spatializer.HEAD_TRACKING_MODE_DISABLED); if (enabled && !mHeadTrackerAvailable) { @@ -1539,10 +1550,12 @@ public class SpatializerHelper { } /*package*/ static final class SADeviceState { + private static boolean sBinauralEnabledDefault = true; + private static boolean sTransauralEnabledDefault = true; private static boolean sHeadTrackingEnabledDefault = false; final @AudioDeviceInfo.AudioDeviceType int mDeviceType; final @NonNull String mDeviceAddress; - boolean mEnabled = true; // by default, SA is enabled on any device + boolean mEnabled; boolean mHasHeadTracker = false; boolean mHeadTrackerEnabled; static final String SETTING_FIELD_SEPARATOR = ","; @@ -1558,6 +1571,12 @@ public class SpatializerHelper { SADeviceState(@AudioDeviceInfo.AudioDeviceType int deviceType, @Nullable String address) { mDeviceType = deviceType; mDeviceAddress = isWireless(deviceType) ? Objects.requireNonNull(address) : ""; + final int spatMode = SPAT_MODE_FOR_DEVICE_TYPE.get(deviceType, Integer.MIN_VALUE); + mEnabled = spatMode == SpatializationMode.SPATIALIZER_BINAURAL + ? sBinauralEnabledDefault + : spatMode == SpatializationMode.SPATIALIZER_TRANSAURAL + ? sTransauralEnabledDefault + : false; mHeadTrackerEnabled = sHeadTrackingEnabledDefault; } @@ -1694,10 +1713,11 @@ public class SpatializerHelper { private int getHeadSensorHandleUpdateTracker() { int headHandle = -1; - final AudioDeviceAttributes currentDevice = ROUTING_DEVICES[0]; - if (currentDevice == null) { + if (sRoutingDevices.isEmpty()) { + logloge("getHeadSensorHandleUpdateTracker: no device, no head tracker"); return headHandle; } + final AudioDeviceAttributes currentDevice = sRoutingDevices.get(0); UUID routingDeviceUuid = mAudioService.getDeviceSensorUuid(currentDevice); // We limit only to Sensor.TYPE_HEAD_TRACKER here to avoid confusion // with gaming sensors. (Note that Sensor.TYPE_ROTATION_VECTOR @@ -1731,6 +1751,23 @@ public class SpatializerHelper { return screenHandle; } + /** + * Returns routing for the given attributes + * @param aa AudioAttributes whose routing is being queried + * @return a non-null never-empty list of devices. If the routing query failed, the list + * will contain null. + */ + private @NonNull ArrayList<AudioDeviceAttributes> getRoutingDevices(AudioAttributes aa) { + final ArrayList<AudioDeviceAttributes> devices = mASA.getDevicesForAttributes( + aa, false /* forVolume */); + for (AudioDeviceAttributes ada : devices) { + if (ada == null) { + // invalid entry, reject this routing query by returning an empty list + return new ArrayList<>(0); + } + } + return devices; + } private static void loglogi(String msg) { AudioService.sSpatialLogger.loglogi(msg, TAG); @@ -1747,4 +1784,13 @@ public class SpatializerHelper { /*package*/ void clearSADevices() { mSADevices.clear(); } + + /*package*/ synchronized void forceStateForTest(int state) { + mState = state; + } + + /*package*/ synchronized void initForTest(boolean hasBinaural, boolean hasTransaural) { + mBinauralSupported = hasBinaural; + mTransauralSupported = hasTransaural; + } } diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 1e9352d10956..b25206d3b621 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -271,6 +271,13 @@ public class Vpn { static final int DEFAULT_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT = 5 * 60; /** + * Default keepalive value to consider long-lived TCP connections are expensive on the + * VPN network from battery usage point of view. + * TODO: consider reading from setting. + */ + @VisibleForTesting + static final int DEFAULT_LONG_LIVED_TCP_CONNS_EXPENSIVE_TIMEOUT_SEC = 60; + /** * Prefer using {@link IkeSessionParams.ESP_IP_VERSION_AUTO} and * {@link IkeSessionParams.ESP_ENCAP_TYPE_AUTO} for ESP packets. * @@ -358,9 +365,8 @@ public class Vpn { return mVpnProfileStore; } - private static final int MAX_EVENTS_LOGS = 20; - private final LocalLog mUnderlyNetworkChanges = new LocalLog(MAX_EVENTS_LOGS); - private final LocalLog mVpnManagerEvents = new LocalLog(MAX_EVENTS_LOGS); + private static final int MAX_EVENTS_LOGS = 100; + private final LocalLog mEventChanges = new LocalLog(MAX_EVENTS_LOGS); /** * Cached Map of <subscription ID, CarrierConfigInfo> since retrieving the PersistableBundle @@ -950,7 +956,7 @@ public class Vpn { int errorCode, @NonNull final String packageName, @Nullable final String sessionKey, @NonNull final VpnProfileState profileState, @Nullable final Network underlyingNetwork, @Nullable final NetworkCapabilities nc, @Nullable final LinkProperties lp) { - mVpnManagerEvents.log("Event class=" + getVpnManagerEventClassName(errorClass) + mEventChanges.log("[VMEvent] Event class=" + getVpnManagerEventClassName(errorClass) + ", err=" + getVpnManagerEventErrorName(errorCode) + " for " + packageName + " on session " + sessionKey); final Intent intent = buildVpnManagerEventIntent(category, errorClass, errorCode, @@ -1100,6 +1106,8 @@ public class Vpn { mLockdownAllowlist = (mLockdown && lockdownAllowlist != null) ? Collections.unmodifiableList(new ArrayList<>(lockdownAllowlist)) : Collections.emptyList(); + mEventChanges.log("[LockdownAlwaysOn] Mode changed: lockdown=" + mLockdown + " alwaysOn=" + + mAlwaysOn + " calling from " + Binder.getCallingUid()); if (isCurrentPreparedPackage(packageName)) { updateAlwaysOnNotification(mNetworkInfo.getDetailedState()); @@ -1670,9 +1678,12 @@ public class Vpn { capsBuilder.setUids(createUserAndRestrictedProfilesRanges(mUserId, mConfig.allowedApplications, mConfig.disallowedApplications)); - capsBuilder.setTransportInfo( - new VpnTransportInfo(getActiveVpnType(), mConfig.session, mConfig.allowBypass, - false /* longLivedTcpConnectionsExpensive */)); + final boolean expensive = areLongLivedTcpConnectionsExpensive(mVpnRunner); + capsBuilder.setTransportInfo(new VpnTransportInfo( + getActiveVpnType(), + mConfig.session, + mConfig.allowBypass, + expensive)); // Only apps targeting Q and above can explicitly declare themselves as metered. // These VPNs are assumed metered unless they state otherwise. @@ -1704,6 +1715,17 @@ public class Vpn { updateState(DetailedState.CONNECTED, "agentConnect"); } + private static boolean areLongLivedTcpConnectionsExpensive(@NonNull VpnRunner runner) { + if (!(runner instanceof IkeV2VpnRunner)) return false; + + final int delay = ((IkeV2VpnRunner) runner).getOrGuessKeepaliveDelaySeconds(); + return areLongLivedTcpConnectionsExpensive(delay); + } + + private static boolean areLongLivedTcpConnectionsExpensive(int keepaliveDelaySec) { + return keepaliveDelaySec < DEFAULT_LONG_LIVED_TCP_CONNS_EXPENSIVE_TIMEOUT_SEC; + } + private boolean canHaveRestrictedProfile(int userId) { final long token = Binder.clearCallingIdentity(); try { @@ -1715,7 +1737,7 @@ public class Vpn { } private void logUnderlyNetworkChanges(List<Network> networks) { - mUnderlyNetworkChanges.log("Switch to " + mEventChanges.log("[UnderlyingNW] Switch to " + ((networks != null) ? TextUtils.join(", ", networks) : "null")); } @@ -2982,16 +3004,17 @@ public class Vpn { @Override public void onCarrierConfigChanged(int slotIndex, int subId, int carrierId, int specificCarrierId) { + mEventChanges.log("[CarrierConfig] Changed on slot " + slotIndex + " subId=" + + subId + " carrerId=" + carrierId + + " specificCarrierId=" + specificCarrierId); synchronized (Vpn.this) { mCachedCarrierConfigInfoPerSubId.remove(subId); // Ignore stale runner. if (mVpnRunner != Vpn.IkeV2VpnRunner.this) return; - maybeMigrateIkeSession(mActiveNetwork); + maybeMigrateIkeSessionAndUpdateVpnTransportInfo(mActiveNetwork); } - // TODO: update the longLivedTcpConnectionsExpensive value in the - // networkcapabilities of the VPN network. } }; @@ -3074,6 +3097,8 @@ public class Vpn { */ public void onIkeOpened(int token, @NonNull IkeSessionConfiguration ikeConfiguration) { if (!isActiveToken(token)) { + mEventChanges.log("[IKEEvent-" + mSessionKey + "] onIkeOpened obsolete token=" + + token); Log.d(TAG, "onIkeOpened called for obsolete token " + token); return; } @@ -3081,7 +3106,12 @@ public class Vpn { mMobikeEnabled = ikeConfiguration.isIkeExtensionEnabled( IkeSessionConfiguration.EXTENSION_TYPE_MOBIKE); - onIkeConnectionInfoChanged(token, ikeConfiguration.getIkeSessionConnectionInfo()); + final IkeSessionConnectionInfo info = ikeConfiguration.getIkeSessionConnectionInfo(); + mEventChanges.log("[IKEEvent-" + mSessionKey + "] onIkeOpened token=" + token + + ", localAddr=" + info.getLocalAddress() + + ", network=" + info.getNetwork() + + ", mobikeEnabled= " + mMobikeEnabled); + onIkeConnectionInfoChanged(token, info); } /** @@ -3094,11 +3124,17 @@ public class Vpn { */ public void onIkeConnectionInfoChanged( int token, @NonNull IkeSessionConnectionInfo ikeConnectionInfo) { + if (!isActiveToken(token)) { + mEventChanges.log("[IKEEvent-" + mSessionKey + + "] onIkeConnectionInfoChanged obsolete token=" + token); Log.d(TAG, "onIkeConnectionInfoChanged called for obsolete token " + token); return; } - + mEventChanges.log("[IKEEvent-" + mSessionKey + + "] onIkeConnectionInfoChanged token=" + token + + ", localAddr=" + ikeConnectionInfo.getLocalAddress() + + ", network=" + ikeConnectionInfo.getNetwork()); // The update on VPN and the IPsec tunnel will be done when migration is fully complete // in onChildMigrated mIkeConnectionInfo = ikeConnectionInfo; @@ -3112,6 +3148,8 @@ public class Vpn { */ public void onChildOpened(int token, @NonNull ChildSessionConfiguration childConfig) { if (!isActiveToken(token)) { + mEventChanges.log("[IKEEvent-" + mSessionKey + + "] onChildOpened obsolete token=" + token); Log.d(TAG, "onChildOpened called for obsolete token " + token); // Do nothing; this signals that either: (1) a new/better Network was found, @@ -3121,7 +3159,9 @@ public class Vpn { // sessions are torn down via resetIkeState(). return; } - + mEventChanges.log("[IKEEvent-" + mSessionKey + "] onChildOpened token=" + token + + ", addr=" + TextUtils.join(", ", childConfig.getInternalAddresses()) + + " dns=" + TextUtils.join(", ", childConfig.getInternalDnsServers())); try { final String interfaceName = mTunnelIface.getInterfaceName(); final List<LinkAddress> internalAddresses = childConfig.getInternalAddresses(); @@ -3218,6 +3258,8 @@ public class Vpn { public void onChildTransformCreated( int token, @NonNull IpSecTransform transform, int direction) { if (!isActiveToken(token)) { + mEventChanges.log("[IKEEvent-" + mSessionKey + + "] onChildTransformCreated obsolete token=" + token); Log.d(TAG, "ChildTransformCreated for obsolete token " + token); // Do nothing; this signals that either: (1) a new/better Network was found, @@ -3227,7 +3269,9 @@ public class Vpn { // sessions are torn down via resetIkeState(). return; } - + mEventChanges.log("[IKEEvent-" + mSessionKey + + "] onChildTransformCreated token=" + token + ", direction=" + direction + + ", transform=" + transform); try { mTunnelIface.setUnderlyingNetwork(mIkeConnectionInfo.getNetwork()); @@ -3252,10 +3296,14 @@ public class Vpn { @NonNull IpSecTransform inTransform, @NonNull IpSecTransform outTransform) { if (!isActiveToken(token)) { + mEventChanges.log("[IKEEvent-" + mSessionKey + + "] onChildMigrated obsolete token=" + token); Log.d(TAG, "onChildMigrated for obsolete token " + token); return; } - + mEventChanges.log("[IKEEvent-" + mSessionKey + + "] onChildMigrated token=" + token + + ", in=" + inTransform + ", out=" + outTransform); // The actual network of this IKE session has migrated to is // mIkeConnectionInfo.getNetwork() instead of mActiveNetwork because mActiveNetwork // might have been updated after the migration was triggered. @@ -3442,7 +3490,7 @@ public class Vpn { return; } - if (maybeMigrateIkeSession(underlyingNetwork)) return; + if (maybeMigrateIkeSessionAndUpdateVpnTransportInfo(underlyingNetwork)) return; startIkeSession(underlyingNetwork); } @@ -3549,7 +3597,43 @@ public class Vpn { return new CarrierConfigInfo(mccMnc, natKeepalive, encapType, ipVersion); } - boolean maybeMigrateIkeSession(@NonNull Network underlyingNetwork) { + private int getOrGuessKeepaliveDelaySeconds() { + if (mProfile.isAutomaticNattKeepaliveTimerEnabled()) { + return guessNattKeepaliveTimerForNetwork(); + } else if (mProfile.getIkeTunnelConnectionParams() != null) { + return mProfile.getIkeTunnelConnectionParams() + .getIkeSessionParams().getNattKeepAliveDelaySeconds(); + } + return DEFAULT_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT; + } + + boolean maybeMigrateIkeSessionAndUpdateVpnTransportInfo( + @NonNull Network underlyingNetwork) { + final int keepaliveDelaySec = getOrGuessKeepaliveDelaySeconds(); + final boolean migrated = maybeMigrateIkeSession(underlyingNetwork, keepaliveDelaySec); + if (migrated) { + updateVpnTransportInfoAndNetCap(keepaliveDelaySec); + } + return migrated; + } + + public void updateVpnTransportInfoAndNetCap(int keepaliveDelaySec) { + final VpnTransportInfo info = new VpnTransportInfo( + getActiveVpnType(), + mConfig.session, + mConfig.allowBypass, + areLongLivedTcpConnectionsExpensive(keepaliveDelaySec)); + final boolean ncUpdateRequired = !info.equals(mNetworkCapabilities.getTransportInfo()); + if (ncUpdateRequired) { + mNetworkCapabilities = new NetworkCapabilities.Builder(mNetworkCapabilities) + .setTransportInfo(info) + .build(); + doSendNetworkCapabilities(mNetworkAgent, mNetworkCapabilities); + } + } + + private boolean maybeMigrateIkeSession(@NonNull Network underlyingNetwork, + int keepaliveDelaySeconds) { if (mSession == null || !mMobikeEnabled) return false; // IKE session can schedule a migration event only when IKE AUTH is finished @@ -3574,15 +3658,6 @@ public class Vpn { encapType = ESP_ENCAP_TYPE_AUTO; } - final int keepaliveDelaySeconds; - if (mProfile.isAutomaticNattKeepaliveTimerEnabled()) { - keepaliveDelaySeconds = guessNattKeepaliveTimerForNetwork(); - } else if (mProfile.getIkeTunnelConnectionParams() != null) { - keepaliveDelaySeconds = mProfile.getIkeTunnelConnectionParams() - .getIkeSessionParams().getNattKeepAliveDelaySeconds(); - } else { - keepaliveDelaySeconds = DEFAULT_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT; - } mSession.setNetwork(underlyingNetwork, ipVersion, encapType, keepaliveDelaySeconds); return true; } @@ -3656,6 +3731,8 @@ public class Vpn { /** Called when the NetworkCapabilities of underlying network is changed */ public void onDefaultNetworkCapabilitiesChanged(@NonNull NetworkCapabilities nc) { + mEventChanges.log("[UnderlyingNW] Cap changed from " + + mUnderlyingNetworkCapabilities + " to " + nc); final NetworkCapabilities oldNc = mUnderlyingNetworkCapabilities; mUnderlyingNetworkCapabilities = nc; if (oldNc == null) { @@ -3663,12 +3740,14 @@ public class Vpn { startOrMigrateIkeSession(mActiveNetwork); } else if (!nc.getSubscriptionIds().equals(oldNc.getSubscriptionIds())) { // Renew carrierConfig values. - maybeMigrateIkeSession(mActiveNetwork); + maybeMigrateIkeSessionAndUpdateVpnTransportInfo(mActiveNetwork); } } /** Called when the LinkProperties of underlying network is changed */ public void onDefaultNetworkLinkPropertiesChanged(@NonNull LinkProperties lp) { + mEventChanges.log("[UnderlyingNW] Lp changed from " + + mUnderlyingLinkProperties + " to " + lp); mUnderlyingLinkProperties = lp; } @@ -3691,7 +3770,7 @@ public class Vpn { Log.d(TAG, "Data stall suspected"); // Trigger MOBIKE. - maybeMigrateIkeSession(mActiveNetwork); + maybeMigrateIkeSessionAndUpdateVpnTransportInfo(mActiveNetwork); mDataStallSuspected = true; } } @@ -4673,7 +4752,7 @@ public class Vpn { // TODO(b/230548427): Remove SDK check once VPN related stuff are decoupled from // ConnectivityServiceTest. if (SdkLevel.isAtLeastT()) { - mVpnManagerEvents.log(packageName + " stopped"); + mEventChanges.log("[VMEvent] " + packageName + " stopped"); sendEventToVpnManagerApp(intent, packageName); } } @@ -5007,23 +5086,21 @@ public class Vpn { pw.println("NetworkCapabilities: " + mNetworkCapabilities); if (isIkev2VpnRunner()) { final IkeV2VpnRunner runner = ((IkeV2VpnRunner) mVpnRunner); - pw.println("Token: " + runner.mSessionKey); + pw.println("SessionKey: " + runner.mSessionKey); pw.println("MOBIKE " + (runner.mMobikeEnabled ? "enabled" : "disabled")); + pw.println("Profile: " + runner.mProfile); + pw.println("Token: " + runner.mCurrentToken); if (mDataStallSuspected) pw.println("Data stall suspected"); if (runner.mScheduledHandleDataStallFuture != null) { pw.println("Reset session scheduled"); } } + pw.println(); pw.println("mCachedCarrierConfigInfoPerSubId=" + mCachedCarrierConfigInfoPerSubId); - pw.println("mUnderlyNetworkChanges (most recent first):"); - pw.increaseIndent(); - mUnderlyNetworkChanges.reverseDump(pw); - pw.decreaseIndent(); - - pw.println("mVpnManagerEvent (most recent first):"); + pw.println("mEventChanges (most recent first):"); pw.increaseIndent(); - mVpnManagerEvents.reverseDump(pw); + mEventChanges.reverseDump(pw); pw.decreaseIndent(); } } diff --git a/services/core/java/com/android/server/vcn/TEST_MAPPING b/services/core/java/com/android/server/vcn/TEST_MAPPING new file mode 100644 index 000000000000..5b04d884fc1a --- /dev/null +++ b/services/core/java/com/android/server/vcn/TEST_MAPPING @@ -0,0 +1,10 @@ +{ + "presubmit": [ + { + "name": "FrameworksVcnTests" + }, + { + "name": "CtsVcnTestCases" + } + ] +}
\ No newline at end of file diff --git a/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java b/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java index 428eaff9e5bc..3ad24de4cdca 100644 --- a/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java +++ b/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java @@ -17,12 +17,17 @@ package com.android.server.audio; import com.android.server.audio.SpatializerHelper.SADeviceState; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; +import android.media.AudioAttributes; import android.media.AudioDeviceAttributes; import android.media.AudioDeviceInfo; +import android.media.AudioFormat; import android.media.AudioSystem; import android.util.Log; @@ -36,6 +41,7 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Spy; +import java.util.ArrayList; import java.util.List; @MediumTest @@ -49,14 +55,35 @@ public class SpatializerHelperTest { @Mock private AudioService mMockAudioService; @Spy private AudioSystemAdapter mSpyAudioSystem; + @Mock private AudioSystemAdapter mMockAudioSystem; @Before public void setUp() throws Exception { mMockAudioService = mock(AudioService.class); - mSpyAudioSystem = spy(new NoOpAudioSystemAdapter()); + } + + /** + * Initializes mSpatHelper, the SpatizerHelper instance under test, to use the mock or spy + * AudioSystemAdapter + * @param useSpyAudioSystem true to use the spy adapter, mSpyAudioSystem, or false to use + * the mock adapter, mMockAudioSystem. + */ + private void setUpSpatHelper(boolean useSpyAudioSystem) { + final AudioSystemAdapter asAdapter; + if (useSpyAudioSystem) { + mSpyAudioSystem = spy(new NoOpAudioSystemAdapter()); + asAdapter = mSpyAudioSystem; + mMockAudioSystem = null; + } else { + mSpyAudioSystem = null; + mMockAudioSystem = mock(NoOpAudioSystemAdapter.class); + asAdapter = mMockAudioSystem; + } + mSpatHelper = new SpatializerHelper(mMockAudioService, asAdapter, + true /*binauralEnabledDefault*/, + true /*transauralEnabledDefault*/, + false /*headTrackingEnabledDefault*/); - mSpatHelper = new SpatializerHelper(mMockAudioService, mSpyAudioSystem, - false /*headTrackingEnabledByDefault*/); } /** @@ -66,6 +93,7 @@ public class SpatializerHelperTest { */ @Test public void testSADeviceStateNullAddressCtor() throws Exception { + setUpSpatHelper(true /*useSpyAudioSystem*/); try { SADeviceState devState = new SADeviceState(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, null); devState = new SADeviceState(AudioDeviceInfo.TYPE_BLUETOOTH_A2DP, null); @@ -76,6 +104,7 @@ public class SpatializerHelperTest { @Test public void testSADeviceStateStringSerialization() throws Exception { Log.i(TAG, "starting testSADeviceStateStringSerialization"); + setUpSpatHelper(true /*useSpyAudioSystem*/); final SADeviceState devState = new SADeviceState( AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, "bla"); devState.mHasHeadTracker = false; @@ -91,6 +120,7 @@ public class SpatializerHelperTest { @Test public void testSADeviceSettings() throws Exception { Log.i(TAG, "starting testSADeviceSettings"); + setUpSpatHelper(true /*useSpyAudioSystem*/); final AudioDeviceAttributes dev1 = new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_SPEAKER, ""); final AudioDeviceAttributes dev2 = @@ -141,4 +171,34 @@ public class SpatializerHelperTest { Log.i(TAG, "device settingsRestored: " + settingsRestored); Assert.assertEquals(settings, settingsRestored); } + + /** + * Test that null devices for routing do not break canBeSpatialized + * @throws Exception + */ + @Test + public void testNoRoutingCanBeSpatialized() throws Exception { + Log.i(TAG, "Starting testNoRoutingCanBeSpatialized"); + setUpSpatHelper(false /*useSpyAudioSystem*/); + mSpatHelper.forceStateForTest(SpatializerHelper.STATE_ENABLED_AVAILABLE); + + final ArrayList<AudioDeviceAttributes> emptyList = new ArrayList<>(0); + final ArrayList<AudioDeviceAttributes> listWithNull = new ArrayList<>(1); + listWithNull.add(null); + final AudioAttributes media = new AudioAttributes.Builder() + .setUsage(AudioAttributes.USAGE_MEDIA).build(); + final AudioFormat spatialFormat = new AudioFormat.Builder() + .setEncoding(AudioFormat.ENCODING_PCM_16BIT) + .setChannelMask(AudioFormat.CHANNEL_OUT_5POINT1).build(); + + when(mMockAudioSystem.getDevicesForAttributes(any(AudioAttributes.class), anyBoolean())) + .thenReturn(emptyList); + Assert.assertFalse("can be spatialized on empty routing", + mSpatHelper.canBeSpatialized(media, spatialFormat)); + + when(mMockAudioSystem.getDevicesForAttributes(any(AudioAttributes.class), anyBoolean())) + .thenReturn(listWithNull); + Assert.assertFalse("can be spatialized on null routing", + mSpatHelper.canBeSpatialized(media, spatialFormat)); + } } diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java index fdf694303dbc..f90eabc7175e 100644 --- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java +++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java @@ -18,6 +18,7 @@ package com.android.internal.telephony; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import android.Manifest; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppOpsManager; import android.content.Context; @@ -822,4 +823,35 @@ public final class TelephonyPermissions { } return Integer.MAX_VALUE; } + + /** + * Check if calling user is associated with the given subscription. + * @param context Context + * @param subId subscription ID + * @param callerUserHandle caller user handle + * @return false if user is not associated with the subscription. + */ + public static boolean checkSubscriptionAssociatedWithUser(@NonNull Context context, int subId, + @NonNull UserHandle callerUserHandle) { + if (!SubscriptionManager.isValidSubscriptionId(subId)) { + // No subscription on device, return true. + return true; + } + + SubscriptionManager subManager = context.getSystemService(SubscriptionManager.class); + final long token = Binder.clearCallingIdentity(); + try { + if ((subManager != null) && + (!subManager.isSubscriptionAssociatedWithUser(subId, callerUserHandle))) { + // If subId is not associated with calling user, return false. + Log.e(LOG_TAG,"User[User ID:" + callerUserHandle.getIdentifier() + + "] is not associated with Subscription ID:" + subId); + return false; + + } + } finally { + Binder.restoreCallingIdentity(token); + } + return true; + } } diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index f570f87f631c..17780af8061c 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -4478,6 +4478,57 @@ public class CarrierConfigManager { "data_stall_recovery_should_skip_bool_array"; /** + * String array containing the list of names for service numbers provided by carriers. This key + * should be used with {@link #KEY_CARRIER_SERVICE_NUMBER_STRING_ARRAY}. The names provided in + * this array will be mapped 1:1 with the numbers provided in the {@link + * #KEY_CARRIER_SERVICE_NUMBER_STRING_ARRAY} array. + * + * <p>The data would be considered valid if and only if: + * + * <ul> + * <li>The number of items in both the arrays are equal + * <li>The data added to the {@link #KEY_CARRIER_SERVICE_NUMBER_STRING_ARRAY} array is valid. + * See {@link #KEY_CARRIER_SERVICE_NUMBER_STRING_ARRAY} for more information. + * </ul> + * + * <p>Example: + * + * <pre><code> + * <string-array name="carrier_service_name_array" num="2"> + * <item value="Police"/> + * <item value="Ambulance"/> + * </string-array> + * </code></pre> + */ + public static final String KEY_CARRIER_SERVICE_NAME_STRING_ARRAY = "carrier_service_name_array"; + + /** + * String array containing the list of service numbers provided by carriers. This key should be + * used with {@link #KEY_CARRIER_SERVICE_NAME_STRING_ARRAY}. The numbers provided in this array + * will be mapped 1:1 with the names provided in the {@link + * #KEY_CARRIER_SERVICE_NAME_STRING_ARRAY} array. + * + * <p>The data would be considered valid if and only if: + * + * <ul> + * <li>The number of items in both the arrays are equal + * <li>The item added in this key follows a specific format. Either it should be all numbers, + * or "+" followed by all numbers. + * </ul> + * + * <p>Example: + * + * <pre><code> + * <string-array name="carrier_service_number_array" num="2"> + * <item value="123"/> + * <item value="+343"/> + * </string-array> + * </code></pre> + */ + public static final String KEY_CARRIER_SERVICE_NUMBER_STRING_ARRAY = + "carrier_service_number_array"; + + /** * Configs used by ImsServiceEntitlement. */ public static final class ImsServiceEntitlement { @@ -9350,6 +9401,8 @@ public class CarrierConfigManager { new long[] {180000, 180000, 180000, 180000}); sDefaults.putBooleanArray(KEY_DATA_STALL_RECOVERY_SHOULD_SKIP_BOOL_ARRAY, new boolean[] {false, false, true, false, false}); + sDefaults.putStringArray(KEY_CARRIER_SERVICE_NAME_STRING_ARRAY, new String[0]); + sDefaults.putStringArray(KEY_CARRIER_SERVICE_NUMBER_STRING_ARRAY, new String[0]); } /** diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java index 1d6798b7fc6e..f1f13bc6bb55 100644 --- a/telephony/java/android/telephony/NetworkRegistrationInfo.java +++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java @@ -184,10 +184,11 @@ public final class NetworkRegistrationInfo implements Parcelable { private final int mTransportType; /** - * The initial registration state + * The true registration state of network, This is not affected by any carrier config or + * resource overlay. */ @RegistrationState - private final int mInitialRegistrationState; + private final int mNetworkRegistrationState; /** * The registration state that might have been overridden by config @@ -264,7 +265,7 @@ public final class NetworkRegistrationInfo implements Parcelable { mDomain = domain; mTransportType = transportType; mRegistrationState = registrationState; - mInitialRegistrationState = registrationState; + mNetworkRegistrationState = registrationState; mRoamingType = (registrationState == REGISTRATION_STATE_ROAMING) ? ServiceState.ROAMING_TYPE_UNKNOWN : ServiceState.ROAMING_TYPE_NOT_ROAMING; setAccessNetworkTechnology(accessNetworkTechnology); @@ -320,7 +321,7 @@ public final class NetworkRegistrationInfo implements Parcelable { mDomain = source.readInt(); mTransportType = source.readInt(); mRegistrationState = source.readInt(); - mInitialRegistrationState = source.readInt(); + mNetworkRegistrationState = source.readInt(); mRoamingType = source.readInt(); mAccessNetworkTechnology = source.readInt(); mRejectCause = source.readInt(); @@ -347,7 +348,7 @@ public final class NetworkRegistrationInfo implements Parcelable { mDomain = nri.mDomain; mTransportType = nri.mTransportType; mRegistrationState = nri.mRegistrationState; - mInitialRegistrationState = nri.mInitialRegistrationState; + mNetworkRegistrationState = nri.mNetworkRegistrationState; mRoamingType = nri.mRoamingType; mAccessNetworkTechnology = nri.mAccessNetworkTechnology; mIsUsingCarrierAggregation = nri.mIsUsingCarrierAggregation; @@ -400,40 +401,72 @@ public final class NetworkRegistrationInfo implements Parcelable { } /** - * @return The registration state. + * @return The registration state. Note this value can be affected by the carrier config + * override. * + * @deprecated Use {@link #getNetworkRegistrationState}, which is not affected by any carrier + * config or resource overlay, instead. * @hide */ + @Deprecated @SystemApi public @RegistrationState int getRegistrationState() { return mRegistrationState; } /** - * @return The initial registration state. + * @return The true registration state of network. (This value is not affected by any carrier + * config or resource overlay override). * * @hide */ - public @RegistrationState int getInitialRegistrationState() { - return mInitialRegistrationState; + @SystemApi + public @RegistrationState int getNetworkRegistrationState() { + return mNetworkRegistrationState; } /** - * @return {@code true} if registered on roaming or home network, {@code false} otherwise. + * @return {@code true} if registered on roaming or home network. Note this value can be + * affected by the carrier config override. + * + * @deprecated Use {@link #isNetworkRegistered}, which is not affected by any carrier config or + * resource overlay, instead. */ + @Deprecated public boolean isRegistered() { return mRegistrationState == REGISTRATION_STATE_HOME || mRegistrationState == REGISTRATION_STATE_ROAMING; } /** + * @return {@code true} if registered on roaming or home network, {@code false} otherwise. (This + * value is not affected by any carrier config or resource overlay override). + */ + public boolean isNetworkRegistered() { + return mNetworkRegistrationState == REGISTRATION_STATE_HOME + || mNetworkRegistrationState == REGISTRATION_STATE_ROAMING; + } + + /** * @return {@code true} if searching for service, {@code false} otherwise. + * + * @deprecated Use {@link #isNetworkRegistered}, which is not affected by any carrier config or + * resource overlay, instead. */ + @Deprecated public boolean isSearching() { return mRegistrationState == REGISTRATION_STATE_NOT_REGISTERED_SEARCHING; } /** + * @return {@code true} if searching for service, {@code false} otherwise. (This value is not + * affected by any carrier config or resource overlay override). + */ + public boolean isNetworkSearching() { + return mNetworkRegistrationState == REGISTRATION_STATE_NOT_REGISTERED_SEARCHING; + } + + /** * Get the PLMN-ID for this Network Registration, also known as the RPLMN. * * <p>If the device is registered, this will return the registered PLMN-ID. If registration @@ -450,13 +483,25 @@ public final class NetworkRegistrationInfo implements Parcelable { } /** - * @return {@code true} if registered on roaming network, {@code false} otherwise. + * @return {@code true} if registered on roaming network overridden by config. Note this value + * can be affected by the carrier config override. + * + * @deprecated Use {@link TelephonyDisplayInfo#isRoaming} instead. */ + @Deprecated public boolean isRoaming() { return mRoamingType != ServiceState.ROAMING_TYPE_NOT_ROAMING; } /** + * @return {@code true} if registered on roaming network. (This value is not affected by any + * carrier config or resource overlay override). + */ + public boolean isNetworkRoaming() { + return mNetworkRegistrationState == REGISTRATION_STATE_ROAMING; + } + + /** * @hide * @return {@code true} if in service. */ @@ -486,7 +531,8 @@ public final class NetworkRegistrationInfo implements Parcelable { } /** - * @return the current network roaming type. + * @return the current network roaming type. Note that this value can be possibly overridden by + * the carrier config or resource overlay. * @hide */ @SystemApi @@ -666,8 +712,8 @@ public final class NetworkRegistrationInfo implements Parcelable { .append(" transportType=").append( AccessNetworkConstants.transportTypeToString(mTransportType)) .append(" registrationState=").append(registrationStateToString(mRegistrationState)) - .append(" mInitialRegistrationState=") - .append(registrationStateToString(mInitialRegistrationState)) + .append(" networkRegistrationState=") + .append(registrationStateToString(mNetworkRegistrationState)) .append(" roamingType=").append(ServiceState.roamingTypeToString(mRoamingType)) .append(" accessNetworkTechnology=") .append(TelephonyManager.getNetworkTypeName(mAccessNetworkTechnology)) @@ -688,7 +734,7 @@ public final class NetworkRegistrationInfo implements Parcelable { @Override public int hashCode() { - return Objects.hash(mDomain, mTransportType, mRegistrationState, mInitialRegistrationState, + return Objects.hash(mDomain, mTransportType, mRegistrationState, mNetworkRegistrationState, mRoamingType, mAccessNetworkTechnology, mRejectCause, mEmergencyOnly, mAvailableServices, mCellIdentity, mVoiceSpecificInfo, mDataSpecificInfo, mNrState, mRplmn, mIsUsingCarrierAggregation); @@ -706,7 +752,7 @@ public final class NetworkRegistrationInfo implements Parcelable { return mDomain == other.mDomain && mTransportType == other.mTransportType && mRegistrationState == other.mRegistrationState - && mInitialRegistrationState == other.mInitialRegistrationState + && mNetworkRegistrationState == other.mNetworkRegistrationState && mRoamingType == other.mRoamingType && mAccessNetworkTechnology == other.mAccessNetworkTechnology && mRejectCause == other.mRejectCause @@ -729,7 +775,7 @@ public final class NetworkRegistrationInfo implements Parcelable { dest.writeInt(mDomain); dest.writeInt(mTransportType); dest.writeInt(mRegistrationState); - dest.writeInt(mInitialRegistrationState); + dest.writeInt(mNetworkRegistrationState); dest.writeInt(mRoamingType); dest.writeInt(mAccessNetworkTechnology); dest.writeInt(mRejectCause); @@ -826,7 +872,7 @@ public final class NetworkRegistrationInfo implements Parcelable { private int mTransportType; @RegistrationState - private int mInitialRegistrationState; + private int mNetworkRegistrationState; @NetworkType private int mAccessNetworkTechnology; @@ -856,6 +902,31 @@ public final class NetworkRegistrationInfo implements Parcelable { public Builder() {} /** + * Builder from the existing {@link NetworkRegistrationInfo}. + * + * @param nri The network registration info object. + * @hide + */ + public Builder(@NonNull NetworkRegistrationInfo nri) { + mDomain = nri.mDomain; + mTransportType = nri.mTransportType; + mNetworkRegistrationState = nri.mNetworkRegistrationState; + mAccessNetworkTechnology = nri.mAccessNetworkTechnology; + mRejectCause = nri.mRejectCause; + mEmergencyOnly = nri.mEmergencyOnly; + mAvailableServices = new ArrayList<>(nri.mAvailableServices); + mCellIdentity = nri.mCellIdentity; + if (nri.mDataSpecificInfo != null) { + mDataSpecificRegistrationInfo = new DataSpecificRegistrationInfo( + nri.mDataSpecificInfo); + } + if (nri.mVoiceSpecificInfo != null) { + mVoiceSpecificRegistrationInfo = new VoiceSpecificRegistrationInfo( + nri.mVoiceSpecificInfo); + } + } + + /** * Set the network domain. * * @param domain Network domain. @@ -887,7 +958,7 @@ public final class NetworkRegistrationInfo implements Parcelable { * @return The same instance of the builder. */ public @NonNull Builder setRegistrationState(@RegistrationState int registrationState) { - mInitialRegistrationState = registrationState; + mNetworkRegistrationState = registrationState; return this; } @@ -1006,7 +1077,7 @@ public final class NetworkRegistrationInfo implements Parcelable { */ @SystemApi public @NonNull NetworkRegistrationInfo build() { - return new NetworkRegistrationInfo(mDomain, mTransportType, mInitialRegistrationState, + return new NetworkRegistrationInfo(mDomain, mTransportType, mNetworkRegistrationState, mAccessNetworkTechnology, mRejectCause, mEmergencyOnly, mAvailableServices, mCellIdentity, mRplmn, mVoiceSpecificRegistrationInfo, mDataSpecificRegistrationInfo); diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index ac740166a024..33e6fa70ffd1 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -631,11 +631,17 @@ public class ServiceState implements Parcelable { } /** - * Get current roaming indicator of phone + * Get current roaming indicator of phone. This roaming state could be overridden by the carrier + * config. * (note: not just decoding from TS 27.007 7.2) - * + * @see TelephonyDisplayInfo#isRoaming() for visualization purpose. * @return true if TS 27.007 7.2 roaming is true * and ONS is different from SPN + * @see CarrierConfigManager#KEY_FORCE_HOME_NETWORK_BOOL + * @see CarrierConfigManager#KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY + * @see CarrierConfigManager#KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY + * @see CarrierConfigManager#KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY + * @see CarrierConfigManager#KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY */ public boolean getRoaming() { return getVoiceRoaming() || getDataRoaming(); @@ -650,8 +656,9 @@ public class ServiceState implements Parcelable { public boolean getVoiceRoaming() { return getVoiceRoamingType() != ROAMING_TYPE_NOT_ROAMING; } + /** - * Get current voice network roaming type + * Get current voice roaming type. This roaming type could be overridden by the carrier config. * @return roaming type * @hide */ @@ -701,7 +708,7 @@ public class ServiceState implements Parcelable { } /** - * Get current data network roaming type + * Get current data roaming type. This roaming type could be overridden by the carrier config. * @return roaming type * @hide */ diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java index d670e5592c42..1cf2969ea9b5 100644 --- a/telephony/java/android/telephony/SmsManager.java +++ b/telephony/java/android/telephony/SmsManager.java @@ -2245,6 +2245,7 @@ public final class SmsManager { RESULT_SMS_SEND_RETRY_FAILED, RESULT_REMOTE_EXCEPTION, RESULT_NO_DEFAULT_SMS_APP, + RESULT_USER_NOT_ALLOWED, RESULT_RIL_RADIO_NOT_AVAILABLE, RESULT_RIL_SMS_SEND_FAIL_RETRY, RESULT_RIL_NETWORK_REJECT, @@ -2425,6 +2426,13 @@ public final class SmsManager { */ public static final int RESULT_NO_DEFAULT_SMS_APP = 32; + /** + * User is not associated with the subscription. + * TODO(b/263279115): Make this error code public. + * @hide + */ + public static final int RESULT_USER_NOT_ALLOWED = 33; + // Radio Error results /** diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 37bfa72f9394..b99f3d681585 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -4394,5 +4394,70 @@ public class SubscriptionManager { } return null; } + + /** + * Check if subscription and user are associated with each other. + * + * @param subscriptionId the subId of the subscription + * @param userHandle user handle of the user + * @return {@code true} if subscription is associated with user + * {code true} if there are no subscriptions on device + * else {@code false} if subscription is not associated with user. + * + * @throws IllegalArgumentException if subscription is invalid. + * @throws SecurityException if the caller doesn't have permissions required. + * @throws IllegalStateException if subscription service is not available. + * + * @hide + */ + @RequiresPermission(Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION) + public boolean isSubscriptionAssociatedWithUser(int subscriptionId, + @NonNull UserHandle userHandle) { + if (!isValidSubscriptionId(subscriptionId)) { + throw new IllegalArgumentException("[isSubscriptionAssociatedWithUser]: " + + "Invalid subscriptionId: " + subscriptionId); + } + + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + return iSub.isSubscriptionAssociatedWithUser(subscriptionId, userHandle); + } else { + throw new IllegalStateException("[isSubscriptionAssociatedWithUser]: " + + "subscription service unavailable"); + } + } catch (RemoteException ex) { + ex.rethrowAsRuntimeException(); + } + return false; + } + + /** + * Get list of subscriptions associated with user. + * + * @param userHandle user handle of the user + * @return list of subscriptionInfo associated with the user. + * + * @throws SecurityException if the caller doesn't have permissions required. + * @throws IllegalStateException if subscription service is not available. + * + * @hide + */ + @RequiresPermission(Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION) + public @NonNull List<SubscriptionInfo> getSubscriptionInfoListAssociatedWithUser( + @NonNull UserHandle userHandle) { + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + return iSub.getSubscriptionInfoListAssociatedWithUser(userHandle); + } else { + throw new IllegalStateException("[getSubscriptionInfoListAssociatedWithUser]: " + + "subscription service unavailable"); + } + } catch (RemoteException ex) { + ex.rethrowAsRuntimeException(); + } + return new ArrayList<>(); + } } diff --git a/telephony/java/android/telephony/TelephonyDisplayInfo.java b/telephony/java/android/telephony/TelephonyDisplayInfo.java index f4e2ade643c7..e01b10eed4db 100644 --- a/telephony/java/android/telephony/TelephonyDisplayInfo.java +++ b/telephony/java/android/telephony/TelephonyDisplayInfo.java @@ -87,10 +87,12 @@ public final class TelephonyDisplayInfo implements Parcelable { public static final int OVERRIDE_NETWORK_TYPE_NR_ADVANCED = 5; @NetworkType - private final int mNetworkType; + private final int mNetworkType; @OverrideNetworkType - private final int mOverrideNetworkType; + private final int mOverrideNetworkType; + + private final boolean mIsRoaming; /** * Constructor @@ -98,18 +100,37 @@ public final class TelephonyDisplayInfo implements Parcelable { * @param networkType Current packet-switching cellular network type * @param overrideNetworkType The override network type * + * @deprecated will not use this constructor anymore. * @hide */ + @Deprecated public TelephonyDisplayInfo(@NetworkType int networkType, @OverrideNetworkType int overrideNetworkType) { + this(networkType, overrideNetworkType, false); + } + + /** + * Constructor + * + * @param networkType Current packet-switching cellular network type + * @param overrideNetworkType The override network type + * @param isRoaming True if the device is roaming after override. + * + * @hide + */ + public TelephonyDisplayInfo(@NetworkType int networkType, + @OverrideNetworkType int overrideNetworkType, + boolean isRoaming) { mNetworkType = networkType; mOverrideNetworkType = overrideNetworkType; + mIsRoaming = isRoaming; } /** @hide */ public TelephonyDisplayInfo(Parcel p) { mNetworkType = p.readInt(); mOverrideNetworkType = p.readInt(); + mIsRoaming = p.readBoolean(); } /** @@ -135,10 +156,25 @@ public final class TelephonyDisplayInfo implements Parcelable { return mOverrideNetworkType; } + /** + * Get device is roaming or not. Note the isRoaming is for market branding or visualization + * purposes only. It cannot be treated as the actual roaming device is camped on. + * + * @return True if the device is registered on roaming network overridden by config. + * @see CarrierConfigManager#KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY + * @see CarrierConfigManager#KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY + * @see CarrierConfigManager#KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY + * @see CarrierConfigManager#KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY + */ + public boolean isRoaming() { + return mIsRoaming; + } + @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(mNetworkType); dest.writeInt(mOverrideNetworkType); + dest.writeBoolean(mIsRoaming); } public static final @NonNull Parcelable.Creator<TelephonyDisplayInfo> CREATOR = @@ -165,12 +201,13 @@ public final class TelephonyDisplayInfo implements Parcelable { if (o == null || getClass() != o.getClass()) return false; TelephonyDisplayInfo that = (TelephonyDisplayInfo) o; return mNetworkType == that.mNetworkType - && mOverrideNetworkType == that.mOverrideNetworkType; + && mOverrideNetworkType == that.mOverrideNetworkType + && mIsRoaming == that.mIsRoaming; } @Override public int hashCode() { - return Objects.hash(mNetworkType, mOverrideNetworkType); + return Objects.hash(mNetworkType, mOverrideNetworkType, mIsRoaming); } /** @@ -195,6 +232,7 @@ public final class TelephonyDisplayInfo implements Parcelable { @Override public String toString() { return "TelephonyDisplayInfo {network=" + TelephonyManager.getNetworkTypeName(mNetworkType) - + ", override=" + overrideNetworkTypeToString(mOverrideNetworkType) + "}"; + + ", overrideNetwork=" + overrideNetworkTypeToString(mOverrideNetworkType) + + ", isRoaming=" + mIsRoaming + "}"; } } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index d23b75bd8c45..fd5ec258d697 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -17027,4 +17027,44 @@ public class TelephonyManager { } return TelephonyManager.SIM_STATE_UNKNOWN; } + + /** + * Convert SIM state into string. + * + * @param state SIM state. + * @return SIM state in string format. + * + * @hide + */ + @NonNull + public static String simStateToString(@SimState int state) { + switch (state) { + case TelephonyManager.SIM_STATE_UNKNOWN: + return "UNKNOWN"; + case TelephonyManager.SIM_STATE_ABSENT: + return "ABSENT"; + case TelephonyManager.SIM_STATE_PIN_REQUIRED: + return "PIN_REQUIRED"; + case TelephonyManager.SIM_STATE_PUK_REQUIRED: + return "PUK_REQUIRED"; + case TelephonyManager.SIM_STATE_NETWORK_LOCKED: + return "NETWORK_LOCKED"; + case TelephonyManager.SIM_STATE_READY: + return "READY"; + case TelephonyManager.SIM_STATE_NOT_READY: + return "NOT_READY"; + case TelephonyManager.SIM_STATE_PERM_DISABLED: + return "PERM_DISABLED"; + case TelephonyManager.SIM_STATE_CARD_IO_ERROR: + return "CARD_IO_ERROR"; + case TelephonyManager.SIM_STATE_CARD_RESTRICTED: + return "CARD_RESTRICTED"; + case TelephonyManager.SIM_STATE_LOADED: + return "LOADED"; + case TelephonyManager.SIM_STATE_PRESENT: + return "PRESENT"; + default: + return "UNKNOWN(" + state + ")"; + } + } } diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl index c5f6902062ff..25a714a4bb81 100644 --- a/telephony/java/com/android/internal/telephony/ISub.aidl +++ b/telephony/java/com/android/internal/telephony/ISub.aidl @@ -326,4 +326,34 @@ interface ISub { * @throws IllegalArgumentException if subId is invalid. */ UserHandle getSubscriptionUserHandle(int subId); + + /** + * Check if subscription and user are associated with each other. + * + * @param subscriptionId the subId of the subscription + * @param userHandle user handle of the user + * @return {@code true} if subscription is associated with user + * {code true} if there are no subscriptions on device + * else {@code false} if subscription is not associated with user. + * + * @throws IllegalArgumentException if subscription is invalid. + * @throws SecurityException if the caller doesn't have permissions required. + * @throws IllegalStateException if subscription service is not available. + * + * @hide + */ + boolean isSubscriptionAssociatedWithUser(int subscriptionId, in UserHandle userHandle); + + /** + * Get list of subscriptions associated with user. + * + * @param userHandle user handle of the user + * @return list of subscriptionInfo associated with the user. + * + * @throws SecurityException if the caller doesn't have permissions required. + * @throws IllegalStateException if subscription service is not available. + * + * @hide + */ + List<SubscriptionInfo> getSubscriptionInfoListAssociatedWithUser(in UserHandle userHandle); } diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java index 075bc5e5214e..4123f8070e36 100644 --- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java +++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java @@ -17,7 +17,9 @@ package com.android.server; import static android.net.ConnectivityManager.NetworkCallback; +import static android.net.NetworkCapabilities.NET_CAPABILITY_ENTERPRISE; import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS; +import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; @@ -67,7 +69,6 @@ import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkRequest; -import android.net.TelephonyNetworkSpecifier; import android.net.Uri; import android.net.vcn.IVcnStatusCallback; import android.net.vcn.IVcnUnderlyingNetworkPolicyListener; @@ -128,6 +129,15 @@ public class VcnManagementServiceTest { private static final VcnConfig TEST_VCN_CONFIG; private static final VcnConfig TEST_VCN_CONFIG_PKG_2; private static final int TEST_UID = Process.FIRST_APPLICATION_UID; + private static final String TEST_IFACE_NAME = "TEST_IFACE"; + private static final String TEST_IFACE_NAME_2 = "TEST_IFACE2"; + private static final LinkProperties TEST_LP_1 = new LinkProperties(); + private static final LinkProperties TEST_LP_2 = new LinkProperties(); + + static { + TEST_LP_1.setInterfaceName(TEST_IFACE_NAME); + TEST_LP_2.setInterfaceName(TEST_IFACE_NAME_2); + } static { final Context mockConfigContext = mock(Context.class); @@ -1034,8 +1044,7 @@ public class VcnManagementServiceTest { setupSubscriptionAndStartVcn(subId, subGrp, isVcnActive); return mVcnMgmtSvc.getUnderlyingNetworkPolicy( - getNetworkCapabilitiesBuilderForTransport(subId, transport).build(), - new LinkProperties()); + getNetworkCapabilitiesBuilderForTransport(subId, transport).build(), TEST_LP_1); } private void checkGetRestrictedTransportsFromCarrierConfig( @@ -1260,7 +1269,7 @@ public class VcnManagementServiceTest { false /* expectRestricted */); } - private void setupTrackedCarrierWifiNetwork(NetworkCapabilities caps) { + private void setupTrackedNetwork(NetworkCapabilities caps, LinkProperties lp) { mVcnMgmtSvc.systemReady(); final ArgumentCaptor<NetworkCallback> captor = @@ -1269,7 +1278,10 @@ public class VcnManagementServiceTest { .registerNetworkCallback( eq(new NetworkRequest.Builder().clearCapabilities().build()), captor.capture()); - captor.getValue().onCapabilitiesChanged(mock(Network.class, CALLS_REAL_METHODS), caps); + + Network mockNetwork = mock(Network.class, CALLS_REAL_METHODS); + captor.getValue().onCapabilitiesChanged(mockNetwork, caps); + captor.getValue().onLinkPropertiesChanged(mockNetwork, lp); } @Test @@ -1279,7 +1291,7 @@ public class VcnManagementServiceTest { getNetworkCapabilitiesBuilderForTransport(TEST_SUBSCRIPTION_ID, TRANSPORT_WIFI) .removeCapability(NET_CAPABILITY_NOT_RESTRICTED) .build(); - setupTrackedCarrierWifiNetwork(existingNetworkCaps); + setupTrackedNetwork(existingNetworkCaps, TEST_LP_1); // Trigger test without VCN instance alive; expect restart due to change of NOT_RESTRICTED // immutable capability @@ -1288,7 +1300,7 @@ public class VcnManagementServiceTest { getNetworkCapabilitiesBuilderForTransport( TEST_SUBSCRIPTION_ID, TRANSPORT_WIFI) .build(), - new LinkProperties()); + TEST_LP_1); assertTrue(policy.isTeardownRequested()); } @@ -1298,7 +1310,7 @@ public class VcnManagementServiceTest { final NetworkCapabilities existingNetworkCaps = getNetworkCapabilitiesBuilderForTransport(TEST_SUBSCRIPTION_ID, TRANSPORT_WIFI) .build(); - setupTrackedCarrierWifiNetwork(existingNetworkCaps); + setupTrackedNetwork(existingNetworkCaps, TEST_LP_1); final VcnUnderlyingNetworkPolicy policy = startVcnAndGetPolicyForTransport( @@ -1315,7 +1327,7 @@ public class VcnManagementServiceTest { .addCapability(NET_CAPABILITY_NOT_RESTRICTED) .removeCapability(NET_CAPABILITY_IMS) .build(); - setupTrackedCarrierWifiNetwork(existingNetworkCaps); + setupTrackedNetwork(existingNetworkCaps, TEST_LP_1); final VcnUnderlyingNetworkPolicy policy = mVcnMgmtSvc.getUnderlyingNetworkPolicy( @@ -1336,7 +1348,7 @@ public class VcnManagementServiceTest { new NetworkCapabilities.Builder() .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) - .setNetworkSpecifier(new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID_2)) + .setSubscriptionIds(Collections.singleton(TEST_SUBSCRIPTION_ID_2)) .build(); VcnUnderlyingNetworkPolicy policy = @@ -1346,6 +1358,38 @@ public class VcnManagementServiceTest { assertEquals(nc, policy.getMergedNetworkCapabilities()); } + /** + * Checks that networks with similar capabilities do not clobber each other. + * + * <p>In previous iterations, the VcnMgmtSvc used capability-matching to check if a network + * undergoing policy checks were the same as an existing networks. However, this meant that if + * there were newly added capabilities that the VCN did not check, two networks differing only + * by that capability would restart each other constantly. + */ + @Test + public void testGetUnderlyingNetworkPolicySimilarNetworks() throws Exception { + NetworkCapabilities nc1 = + new NetworkCapabilities.Builder() + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) + .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) + .addCapability(NET_CAPABILITY_INTERNET) + .setSubscriptionIds(Collections.singleton(TEST_SUBSCRIPTION_ID_2)) + .build(); + + NetworkCapabilities nc2 = + new NetworkCapabilities.Builder(nc1) + .addCapability(NET_CAPABILITY_ENTERPRISE) + .removeCapability(NET_CAPABILITY_NOT_RESTRICTED) + .build(); + + setupTrackedNetwork(nc1, TEST_LP_1); + + VcnUnderlyingNetworkPolicy policy = mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc2, TEST_LP_2); + + assertFalse(policy.isTeardownRequested()); + assertEquals(nc2, policy.getMergedNetworkCapabilities()); + } + @Test(expected = SecurityException.class) public void testGetUnderlyingNetworkPolicyInvalidPermission() { doReturn(PackageManager.PERMISSION_DENIED) |