diff options
18 files changed, 469 insertions, 61 deletions
diff --git a/api/Android.bp b/api/Android.bp index 20528f2d67ee..4cb52bc2d29a 100644 --- a/api/Android.bp +++ b/api/Android.bp @@ -257,3 +257,15 @@ genrule { out: ["combined-removed-dex.txt"], cmd: "$(location gen_combined_removed_dex.sh) $(location metalava) $(genDir) $(in) > $(out)", } + +java_genrule { + name: "api_fingerprint", + srcs: [ + ":frameworks-base-api-current.txt", + ":frameworks-base-api-system-current.txt", + ":frameworks-base-api-module-lib-current.txt", + ":frameworks-base-api-system-server-current.txt", + ], + out: ["api_fingerprint.txt"], + cmd: "cat $(in) | md5sum | cut -d' ' -f1 > $(out)", +} diff --git a/core/api/current.txt b/core/api/current.txt index 6ed5eb83db9a..729d8dfa906a 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -41301,6 +41301,8 @@ package android.telephony { method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_STATE, "carrier privileges"}) public android.os.PersistableBundle getConfigForSubId(int, @NonNull java.lang.String...); method public static boolean isConfigForIdentifiedCarrier(android.os.PersistableBundle); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyConfigChangedForSubId(int); + method public void registerCarrierConfigChangeListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.CarrierConfigManager.CarrierConfigChangeListener); + method public void unregisterCarrierConfigChangeListener(@NonNull android.telephony.CarrierConfigManager.CarrierConfigChangeListener); field public static final String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED"; field public static final int CARRIER_NR_AVAILABILITY_NSA = 1; // 0x1 field public static final int CARRIER_NR_AVAILABILITY_SA = 2; // 0x2 @@ -41615,6 +41617,10 @@ package android.telephony { field public static final String KEY_PREFIX = "bsf."; } + public static interface CarrierConfigManager.CarrierConfigChangeListener { + method public void onCarrierConfigChanged(int, int, int, int); + } + public static final class CarrierConfigManager.Gps { field public static final String KEY_PERSIST_LPP_MODE_BOOL = "gps.persist_lpp_mode_bool"; field public static final String KEY_PREFIX = "gps."; diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java index 1bb44af81cec..656cd997acbe 100644 --- a/core/java/android/nfc/NfcAdapter.java +++ b/core/java/android/nfc/NfcAdapter.java @@ -419,6 +419,7 @@ public final class NfcAdapter { static boolean sIsInitialized = false; static boolean sHasNfcFeature; static boolean sHasBeamFeature; + static boolean sHasCeFeature; // Final after first constructor, except for // attemptDeadServiceRecovery() when NFC crashes - we accept a best effort @@ -616,11 +617,13 @@ public final class NfcAdapter { pm = context.getPackageManager(); sHasNfcFeature = pm.hasSystemFeature(PackageManager.FEATURE_NFC); sHasBeamFeature = pm.hasSystemFeature(PackageManager.FEATURE_NFC_BEAM); - boolean hasHceFeature = + sHasCeFeature = pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION) - || pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF); + || pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF) + || pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC) + || pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE); /* is this device meant to have NFC */ - if (!sHasNfcFeature && !hasHceFeature) { + if (!sHasNfcFeature && !sHasCeFeature) { Log.v(TAG, "this device does not have NFC support"); throw new UnsupportedOperationException(); } @@ -643,7 +646,7 @@ public final class NfcAdapter { throw new UnsupportedOperationException(); } } - if (hasHceFeature) { + if (sHasCeFeature) { try { sNfcFCardEmulationService = sService.getNfcFCardEmulationInterface(); } catch (RemoteException e) { @@ -1846,7 +1849,7 @@ public final class NfcAdapter { @SystemApi @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableSecureNfc(boolean enable) { - if (!sHasNfcFeature) { + if (!sHasNfcFeature && !sHasCeFeature) { throw new UnsupportedOperationException(); } try { @@ -1871,10 +1874,13 @@ public final class NfcAdapter { * Checks if the device supports Secure NFC functionality. * * @return True if device supports Secure NFC, false otherwise - * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. + * @throws UnsupportedOperationException if FEATURE_NFC, + * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF, + * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE + * are unavailable */ public boolean isSecureNfcSupported() { - if (!sHasNfcFeature) { + if (!sHasNfcFeature && !sHasCeFeature) { throw new UnsupportedOperationException(); } try { @@ -1900,11 +1906,14 @@ public final class NfcAdapter { * such as their relative positioning on the device. * * @return Information on the nfc antenna(s) on the device. - * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. + * @throws UnsupportedOperationException if FEATURE_NFC, + * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF, + * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE + * are unavailable */ @Nullable public NfcAntennaInfo getNfcAntennaInfo() { - if (!sHasNfcFeature) { + if (!sHasNfcFeature && !sHasCeFeature) { throw new UnsupportedOperationException(); } try { @@ -1929,12 +1938,15 @@ public final class NfcAdapter { * Checks Secure NFC feature is enabled. * * @return True if Secure NFC is enabled, false otherwise - * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. + * @throws UnsupportedOperationException if FEATURE_NFC, + * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF, + * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE + * are unavailable * @throws UnsupportedOperationException if device doesn't support * Secure NFC functionality. {@link #isSecureNfcSupported} */ public boolean isSecureNfcEnabled() { - if (!sHasNfcFeature) { + if (!sHasNfcFeature && !sHasCeFeature) { throw new UnsupportedOperationException(); } try { @@ -2281,14 +2293,17 @@ public final class NfcAdapter { * always on. * @param value if true the NFCC will be kept on (with no RF enabled if NFC adapter is * disabled), if false the NFCC will follow completely the Nfc adapter state. - * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. + * @throws UnsupportedOperationException if FEATURE_NFC, + * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF, + * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE + * are unavailable * @return void * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean setControllerAlwaysOn(boolean value) { - if (!sHasNfcFeature) { + if (!sHasNfcFeature && !sHasCeFeature) { throw new UnsupportedOperationException(); } try { @@ -2313,7 +2328,10 @@ public final class NfcAdapter { * Checks NFC controller always on feature is enabled. * * @return True if NFC controller always on is enabled, false otherwise - * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. + * @throws UnsupportedOperationException if FEATURE_NFC, + * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF, + * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE + * are unavailable * @hide */ @SystemApi @@ -2341,13 +2359,16 @@ public final class NfcAdapter { * Checks if the device supports NFC controller always on functionality. * * @return True if device supports NFC controller always on, false otherwise - * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. + * @throws UnsupportedOperationException if FEATURE_NFC, + * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF, + * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE + * are unavailable * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOnSupported() { - if (!sHasNfcFeature) { + if (!sHasNfcFeature && !sHasCeFeature) { throw new UnsupportedOperationException(); } try { diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java index a3696e398668..4bcdf0d0d50d 100644 --- a/core/java/android/telephony/TelephonyRegistryManager.java +++ b/core/java/android/telephony/TelephonyRegistryManager.java @@ -45,6 +45,7 @@ import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.internal.listeners.ListenerExecutor; +import com.android.internal.telephony.ICarrierConfigChangeListener; import com.android.internal.telephony.ICarrierPrivilegesCallback; import com.android.internal.telephony.IOnSubscriptionsChangedListener; import com.android.internal.telephony.ITelephonyRegistry; @@ -54,8 +55,10 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.WeakHashMap; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.stream.Collectors; @@ -89,6 +92,14 @@ public class TelephonyRegistryManager { IOnSubscriptionsChangedListener> mOpportunisticSubscriptionChangedListenerMap = new HashMap<>(); + /** + * A mapping between {@link CarrierConfigManager.CarrierConfigChangeListener} and its callback + * ICarrierConfigChangeListener. + */ + private final ConcurrentHashMap<CarrierConfigManager.CarrierConfigChangeListener, + ICarrierConfigChangeListener> + mCarrierConfigChangeListenerMap = new ConcurrentHashMap<>(); + /** @hide **/ public TelephonyRegistryManager(@NonNull Context context) { @@ -1409,4 +1420,94 @@ public class TelephonyRegistryManager { throw e.rethrowFromSystemServer(); } } + + /** + * Register a {@link android.telephony.CarrierConfigManager.CarrierConfigChangeListener} to get + * notification when carrier configurations have changed. + * + * @param executor The executor on which the callback will be executed. + * @param listener The CarrierConfigChangeListener to be registered with. + */ + public void addCarrierConfigChangedListener( + @NonNull @CallbackExecutor Executor executor, + @NonNull CarrierConfigManager.CarrierConfigChangeListener listener) { + Objects.requireNonNull(executor, "Executor should be non-null."); + Objects.requireNonNull(listener, "Listener should be non-null."); + if (mCarrierConfigChangeListenerMap.get(listener) != null) { + Log.e(TAG, "registerCarrierConfigChangeListener: listener already present"); + return; + } + + ICarrierConfigChangeListener callback = new ICarrierConfigChangeListener.Stub() { + @Override + public void onCarrierConfigChanged(int slotIndex, int subId, int carrierId, + int specificCarrierId) { + Log.d(TAG, "onCarrierConfigChanged call in ICarrierConfigChangeListener callback"); + final long identify = Binder.clearCallingIdentity(); + try { + executor.execute(() -> listener.onCarrierConfigChanged(slotIndex, subId, + carrierId, specificCarrierId)); + } finally { + Binder.restoreCallingIdentity(identify); + } + } + }; + + try { + sRegistry.addCarrierConfigChangeListener(callback, + mContext.getOpPackageName(), mContext.getAttributionTag()); + mCarrierConfigChangeListenerMap.put(listener, callback); + } catch (RemoteException re) { + // system server crashes + throw re.rethrowFromSystemServer(); + } + } + + /** + * Unregister to stop the notification when carrier configurations changed. + * + * @param listener The CarrierConfigChangeListener to be unregistered with. + */ + public void removeCarrierConfigChangedListener( + @NonNull CarrierConfigManager.CarrierConfigChangeListener listener) { + Objects.requireNonNull(listener, "Listener should be non-null."); + if (mCarrierConfigChangeListenerMap.get(listener) == null) { + Log.e(TAG, "removeCarrierConfigChangedListener: listener was not present"); + return; + } + + try { + sRegistry.removeCarrierConfigChangeListener( + mCarrierConfigChangeListenerMap.get(listener), mContext.getOpPackageName()); + mCarrierConfigChangeListenerMap.remove(listener); + } catch (RemoteException re) { + // System sever crashes + throw re.rethrowFromSystemServer(); + } + } + + /** + * Notify the registrants the carrier configurations have changed. + * + * @param slotIndex The SIM slot index on which to monitor and get notification. + * @param subId The subscription on the SIM slot. May be + * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}. + * @param carrierId The optional carrier Id, may be + * {@link TelephonyManager#UNKNOWN_CARRIER_ID}. + * @param specificCarrierId The optional specific carrier Id, may be {@link + * TelephonyManager#UNKNOWN_CARRIER_ID}. + */ + public void notifyCarrierConfigChanged(int slotIndex, int subId, int carrierId, + int specificCarrierId) { + // Only validate slotIndex, all others are optional and allowed to be invalid + if (!SubscriptionManager.isValidPhoneId(slotIndex)) { + Log.e(TAG, "notifyCarrierConfigChanged, ignored: invalid slotIndex " + slotIndex); + return; + } + try { + sRegistry.notifyCarrierConfigChanged(slotIndex, subId, carrierId, specificCarrierId); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } } diff --git a/core/java/com/android/internal/telephony/ICarrierConfigChangeListener.aidl b/core/java/com/android/internal/telephony/ICarrierConfigChangeListener.aidl new file mode 100644 index 000000000000..0f7ab0a3d4fb --- /dev/null +++ b/core/java/com/android/internal/telephony/ICarrierConfigChangeListener.aidl @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony; + +oneway interface ICarrierConfigChangeListener { + void onCarrierConfigChanged(int slotIndex, int subId, int carrierId, int specificCarrierId); +}
\ No newline at end of file diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl index c7fa757ac0b7..747c40df9492 100644 --- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl +++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl @@ -32,6 +32,7 @@ import android.telephony.PreciseDataConnectionState; import android.telephony.ServiceState; import android.telephony.SignalStrength; import android.telephony.emergency.EmergencyNumber; +import com.android.internal.telephony.ICarrierConfigChangeListener; import com.android.internal.telephony.ICarrierPrivilegesCallback; import com.android.internal.telephony.IPhoneStateListener; import com.android.internal.telephony.IOnSubscriptionsChangedListener; @@ -109,4 +110,8 @@ interface ITelephonyRegistry { int phoneId, in List<String> privilegedPackageNames, in int[] privilegedUids); void notifyCarrierServiceChanged(int phoneId, in String packageName, int uid); + void addCarrierConfigChangeListener(ICarrierConfigChangeListener listener, + String pkg, String featureId); + void removeCarrierConfigChangeListener(ICarrierConfigChangeListener listener, String pkg); + void notifyCarrierConfigChanged(int phoneId, int subId, int carrierId, int specificCarrierId); } diff --git a/core/tests/coretests/src/android/content/pm/TEST_MAPPING b/core/tests/coretests/src/android/content/pm/TEST_MAPPING index 15e04d11acf7..978d80cb52f6 100644 --- a/core/tests/coretests/src/android/content/pm/TEST_MAPPING +++ b/core/tests/coretests/src/android/content/pm/TEST_MAPPING @@ -1,5 +1,5 @@ { - "presubmit-large": [ + "presubmit": [ { "name": "FrameworksCoreTests", "options": [ diff --git a/core/tests/coretests/src/android/view/OWNERS b/core/tests/coretests/src/android/view/OWNERS index a142e27a1ab0..2ca99943a8a0 100644 --- a/core/tests/coretests/src/android/view/OWNERS +++ b/core/tests/coretests/src/android/view/OWNERS @@ -1,4 +1,5 @@ # Accessibility +per-file AccessibilityInteractionControllerTest.java = file:/services/accessibility/OWNERS per-file WindowInfoTest.java = file:/services/accessibility/OWNERS # Input diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java index ca3c84729388..6e60e9e2df6a 100644 --- a/graphics/java/android/graphics/ColorSpace.java +++ b/graphics/java/android/graphics/ColorSpace.java @@ -170,7 +170,7 @@ public abstract class ColorSpace { /** * Standard CIE 1931 2° illuminant D65, encoded in xyY. * This illuminant has a color temperature of 6504K. This illuminant - * is commonly used in RGB color spaces such as sRGB, BT.209, etc. + * is commonly used in RGB color spaces such as sRGB, BT.709, etc. */ public static final float[] ILLUMINANT_D65 = { 0.31271f, 0.32902f }; /** @@ -832,8 +832,8 @@ public abstract class ColorSpace { public enum Model { /** * The RGB model is a color model with 3 components that - * refer to the three additive primiaries: red, green - * andd blue. + * refer to the three additive primaries: red, green + * and blue. */ RGB(3), /** @@ -2405,7 +2405,7 @@ public abstract class ColorSpace { * does not need to be specified and is assumed to be 1.0. Only the xy components * are required.</p> * - * <p class="note">The ID, areturned by {@link #getId()}, of an object created by + * <p class="note">The ID, as returned by {@link #getId()}, of an object created by * this constructor is always {@link #MIN_ID}.</p> * * @param name Name of the color space, cannot be null, its length must be >= 1 @@ -3832,7 +3832,7 @@ public abstract class ColorSpace { * * <p>We can only connect color spaces if they use the same profile * connection space. We assume the connection space is always - * CIE XYZ but we maye need to perform a chromatic adaptation to + * CIE XYZ but we maybe need to perform a chromatic adaptation to * match the white points. If an adaptation is needed, we use the * CIE standard illuminant D50. The unmatched color space is adapted * using the von Kries transform and the {@link Adaptation#BRADFORD} diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index c8187b89b0ed..8b2eebe8afe5 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -208,7 +208,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> synchronized (mProfileLock) { if (profile instanceof A2dpProfile || profile instanceof HeadsetProfile - || profile instanceof HearingAidProfile) { + || profile instanceof HearingAidProfile || profile instanceof LeAudioProfile) { setProfileConnectedStatus(profile.getProfileId(), false); switch (newProfileState) { case BluetoothProfile.STATE_CONNECTED: @@ -226,7 +226,20 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> case BluetoothProfile.STATE_DISCONNECTED: if (mHandler.hasMessages(profile.getProfileId())) { mHandler.removeMessages(profile.getProfileId()); - setProfileConnectedStatus(profile.getProfileId(), true); + if (profile.getConnectionPolicy(mDevice) > + BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) { + /* + * If we received state DISCONNECTED and previous state was + * CONNECTING and connection policy is FORBIDDEN or UNKNOWN + * then it's not really a failure to connect. + * + * Connection profile is considered as failed when connection + * policy indicates that profile should be connected + * but it got disconnected. + */ + Log.w(TAG, "onProfileStateChanged(): Failed to connect profile"); + setProfileConnectedStatus(profile.getProfileId(), true); + } } break; default: @@ -1188,6 +1201,13 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> } private boolean isProfileConnectedFail() { + Log.d(TAG, "anonymizedAddress=" + mDevice.getAnonymizedAddress() + + " mIsA2dpProfileConnectedFail=" + mIsA2dpProfileConnectedFail + + " mIsHearingAidProfileConnectedFail=" + mIsHearingAidProfileConnectedFail + + " mIsLeAudioProfileConnectedFail=" + mIsLeAudioProfileConnectedFail + + " mIsHeadsetProfileConnectedFail=" + mIsHeadsetProfileConnectedFail + + " isConnectedSapDevice()=" + isConnectedSapDevice()); + return mIsA2dpProfileConnectedFail || mIsHearingAidProfileConnectedFail || (!isConnectedSapDevice() && mIsHeadsetProfileConnectedFail) || mIsLeAudioProfileConnectedFail; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java index 315ab0aac878..df3861a699b0 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java @@ -74,6 +74,8 @@ public class CachedBluetoothDeviceTest { @Mock private HearingAidProfile mHearingAidProfile; @Mock + private LeAudioProfile mLeAudioProfile; + @Mock private BluetoothDevice mDevice; @Mock private BluetoothDevice mSubDevice; @@ -92,15 +94,76 @@ public class CachedBluetoothDeviceTest { mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter()); when(mDevice.getAddress()).thenReturn(DEVICE_ADDRESS); when(mHfpProfile.isProfileReady()).thenReturn(true); + when(mHfpProfile.getProfileId()).thenReturn(BluetoothProfile.HEADSET); when(mA2dpProfile.isProfileReady()).thenReturn(true); + when(mA2dpProfile.getProfileId()).thenReturn(BluetoothProfile.A2DP); when(mPanProfile.isProfileReady()).thenReturn(true); + when(mPanProfile.getProfileId()).thenReturn(BluetoothProfile.PAN); when(mHearingAidProfile.isProfileReady()).thenReturn(true); + when(mHearingAidProfile.getProfileId()).thenReturn(BluetoothProfile.HEARING_AID); + when(mLeAudioProfile.isProfileReady()).thenReturn(true); + when(mLeAudioProfile.getProfileId()).thenReturn(BluetoothProfile.LE_AUDIO); mCachedDevice = spy(new CachedBluetoothDevice(mContext, mProfileManager, mDevice)); mSubCachedDevice = spy(new CachedBluetoothDevice(mContext, mProfileManager, mSubDevice)); doAnswer((invocation) -> mBatteryLevel).when(mCachedDevice).getBatteryLevel(); doAnswer((invocation) -> mBatteryLevel).when(mSubCachedDevice).getBatteryLevel(); } + private void testTransitionFromConnectingToDisconnected( + LocalBluetoothProfile connectingProfile, LocalBluetoothProfile connectedProfile, + int connectionPolicy, String expectedSummary) { + // Arrange: + // At least one profile has to be connected + updateProfileStatus(connectedProfile, BluetoothProfile.STATE_CONNECTED); + // Set profile under test to CONNECTING + updateProfileStatus(connectingProfile, BluetoothProfile.STATE_CONNECTING); + // Set connection policy + when(connectingProfile.getConnectionPolicy(mDevice)).thenReturn(connectionPolicy); + + // Act & Assert: + // Get the expected connection summary. + updateProfileStatus(connectingProfile, BluetoothProfile.STATE_DISCONNECTED); + assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(expectedSummary); + } + + @Test + public void onProfileStateChanged_testConnectingToDisconnected_policyAllowed_problem() { + String connectTimeoutString = mContext.getString(R.string.profile_connect_timeout_subtext); + + testTransitionFromConnectingToDisconnected(mA2dpProfile, mLeAudioProfile, + BluetoothProfile.CONNECTION_POLICY_ALLOWED, connectTimeoutString); + testTransitionFromConnectingToDisconnected(mHearingAidProfile, mLeAudioProfile, + BluetoothProfile.CONNECTION_POLICY_ALLOWED, connectTimeoutString); + testTransitionFromConnectingToDisconnected(mHfpProfile, mLeAudioProfile, + BluetoothProfile.CONNECTION_POLICY_ALLOWED, connectTimeoutString); + testTransitionFromConnectingToDisconnected(mLeAudioProfile, mA2dpProfile, + BluetoothProfile.CONNECTION_POLICY_ALLOWED, connectTimeoutString); + } + + @Test + public void onProfileStateChanged_testConnectingToDisconnected_policyForbidden_noProblem() { + testTransitionFromConnectingToDisconnected(mA2dpProfile, mLeAudioProfile, + BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, null); + testTransitionFromConnectingToDisconnected(mHearingAidProfile, mLeAudioProfile, + BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, null); + testTransitionFromConnectingToDisconnected(mHfpProfile, mLeAudioProfile, + BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, null); + testTransitionFromConnectingToDisconnected(mLeAudioProfile, mA2dpProfile, + BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, null); + } + + @Test + public void onProfileStateChanged_testConnectingToDisconnected_policyUnknown_noProblem() { + testTransitionFromConnectingToDisconnected(mA2dpProfile, mLeAudioProfile, + BluetoothProfile.CONNECTION_POLICY_UNKNOWN, null); + testTransitionFromConnectingToDisconnected(mHearingAidProfile, mLeAudioProfile, + BluetoothProfile.CONNECTION_POLICY_UNKNOWN, null); + testTransitionFromConnectingToDisconnected(mHfpProfile, mLeAudioProfile, + BluetoothProfile.CONNECTION_POLICY_UNKNOWN, null); + testTransitionFromConnectingToDisconnected(mLeAudioProfile, mA2dpProfile, + BluetoothProfile.CONNECTION_POLICY_UNKNOWN, null); + } + @Test public void getConnectionSummary_testProfilesInactive_returnPairing() { // Arrange: diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index ca86021cd629..a69d3f0276f3 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -91,6 +91,7 @@ import android.util.Pair; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; +import com.android.internal.telephony.ICarrierConfigChangeListener; import com.android.internal.telephony.ICarrierPrivilegesCallback; import com.android.internal.telephony.IOnSubscriptionsChangedListener; import com.android.internal.telephony.IPhoneStateListener; @@ -154,6 +155,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { IOnSubscriptionsChangedListener onSubscriptionsChangedListenerCallback; IOnSubscriptionsChangedListener onOpportunisticSubscriptionsChangedListenerCallback; ICarrierPrivilegesCallback carrierPrivilegesCallback; + ICarrierConfigChangeListener carrierConfigChangeListener; int callerUid; int callerPid; @@ -182,6 +184,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { return carrierPrivilegesCallback != null; } + boolean matchCarrierConfigChangeListener() { + return carrierConfigChangeListener != null; + } + boolean canReadCallLog() { try { return TelephonyPermissions.checkReadCallLog( @@ -200,6 +206,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { + " onOpportunisticSubscriptionsChangedListenererCallback=" + onOpportunisticSubscriptionsChangedListenerCallback + " carrierPrivilegesCallback=" + carrierPrivilegesCallback + + " carrierConfigChangeListener=" + carrierConfigChangeListener + " subId=" + subId + " phoneId=" + phoneId + " events=" + eventList + "}"; } } @@ -2955,6 +2962,82 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } + @Override + public void addCarrierConfigChangeListener(ICarrierConfigChangeListener listener, + String pkg, String featureId) { + final int callerUserId = UserHandle.getCallingUserId(); + mAppOps.checkPackage(Binder.getCallingUid(), pkg); + if (VDBG) { + log("addCarrierConfigChangeListener pkg=" + pii(pkg) + " uid=" + Binder.getCallingUid() + + " myUserId=" + UserHandle.myUserId() + " callerUerId" + callerUserId + + " listener=" + listener + " listener.asBinder=" + listener.asBinder()); + } + + synchronized (mRecords) { + IBinder b = listener.asBinder(); + boolean doesLimitApply = doesLimitApplyForListeners(Binder.getCallingUid(), + Process.myUid()); + Record r = add(b, Binder.getCallingUid(), Binder.getCallingPid(), doesLimitApply); + + if (r == null) { + loge("Can not create Record instance!"); + return; + } + + r.context = mContext; + r.carrierConfigChangeListener = listener; + r.callingPackage = pkg; + r.callingFeatureId = featureId; + r.callerUid = Binder.getCallingUid(); + r.callerPid = Binder.getCallingPid(); + r.eventList = new ArraySet<>(); + if (DBG) { + log("addCarrierConfigChangeListener: Register r=" + r); + } + } + } + + @Override + public void removeCarrierConfigChangeListener(ICarrierConfigChangeListener listener, + String pkg) { + if (DBG) log("removeCarrierConfigChangeListener listener=" + listener + ", pkg=" + pkg); + mAppOps.checkPackage(Binder.getCallingUid(), pkg); + remove(listener.asBinder()); + } + + @Override + public void notifyCarrierConfigChanged(int phoneId, int subId, int carrierId, + int specificCarrierId) { + if (!validatePhoneId(phoneId)) { + throw new IllegalArgumentException("Invalid phoneId: " + phoneId); + } + if (!checkNotifyPermission("notifyCarrierConfigChanged")) { + loge("Caller has no notify permission!"); + return; + } + if (VDBG) { + log("notifyCarrierConfigChanged: phoneId=" + phoneId + ", subId=" + subId + + ", carrierId=" + carrierId + ", specificCarrierId=" + specificCarrierId); + } + + synchronized (mRecords) { + mRemoveList.clear(); + for (Record r : mRecords) { + // Listeners are "global", neither per-slot nor per-sub, so no idMatch check here + if (!r.matchCarrierConfigChangeListener()) { + continue; + } + try { + r.carrierConfigChangeListener.onCarrierConfigChanged(phoneId, subId, carrierId, + specificCarrierId); + } catch (RemoteException re) { + mRemoveList.add(r.binder); + } + } + handleRemoveListLocked(); + } + } + @NeverCompile // Avoid size overhead of debugging code. @Override public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index e2c4cbdc829c..dcf19062834c 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -1650,15 +1650,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mContext, 0, snoozeIntent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE)); final Intent viewIntent = buildViewDataUsageIntent(res, policy.template); - // TODO: Resolve to single code path. - if (UserManager.isHeadlessSystemUserMode()) { - builder.setContentIntent(PendingIntent.getActivityAsUser( - mContext, 0, viewIntent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE, - /* options= */ null, UserHandle.CURRENT)); - } else { - builder.setContentIntent(PendingIntent.getActivity( - mContext, 0, viewIntent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE)); - } + setContentIntent(builder, viewIntent); break; } case TYPE_LIMIT: { @@ -1679,15 +1671,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { builder.setSmallIcon(R.drawable.stat_notify_disabled_data); final Intent intent = buildNetworkOverLimitIntent(res, policy.template); - // TODO: Resolve to single code path. - if (UserManager.isHeadlessSystemUserMode()) { - builder.setContentIntent(PendingIntent.getActivityAsUser( - mContext, 0, intent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE, - /* options= */ null, UserHandle.CURRENT)); - } else { - builder.setContentIntent(PendingIntent.getActivity( - mContext, 0, intent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE)); - } + setContentIntent(builder, intent); break; } case TYPE_LIMIT_SNOOZED: { @@ -1711,15 +1695,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { builder.setChannelId(SystemNotificationChannels.NETWORK_STATUS); final Intent intent = buildViewDataUsageIntent(res, policy.template); - // TODO: Resolve to single code path. - if (UserManager.isHeadlessSystemUserMode()) { - builder.setContentIntent(PendingIntent.getActivityAsUser( - mContext, 0, intent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE, - /* options= */ null, UserHandle.CURRENT)); - } else { - builder.setContentIntent(PendingIntent.getActivity( - mContext, 0, intent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE)); - } + setContentIntent(builder, intent); break; } case TYPE_RAPID: { @@ -1739,15 +1715,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mContext, 0, snoozeIntent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE)); final Intent viewIntent = buildViewDataUsageIntent(res, policy.template); - // TODO: Resolve to single code path. - if (UserManager.isHeadlessSystemUserMode()) { - builder.setContentIntent(PendingIntent.getActivityAsUser( - mContext, 0, viewIntent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE, - /* options= */ null, UserHandle.CURRENT)); - } else { - builder.setContentIntent(PendingIntent.getActivity( - mContext, 0, viewIntent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE)); - } + setContentIntent(builder, viewIntent); break; } default: { @@ -1765,6 +1733,17 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mActiveNotifs.add(notificationId); } + private void setContentIntent(Notification.Builder builder, Intent intent) { + if (UserManager.isHeadlessSystemUserMode()) { + builder.setContentIntent(PendingIntent.getActivityAsUser( + mContext, 0, intent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE, + /* options= */ null, UserHandle.CURRENT)); + } else { + builder.setContentIntent(PendingIntent.getActivity( + mContext, 0, intent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE)); + } + } + private void cancelNotification(NotificationId notificationId) { mContext.getSystemService(NotificationManager.class).cancel(notificationId.getTag(), notificationId.getId()); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index b8486e7aa2b4..cae0234bdc7a 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -557,6 +557,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A Drawable mEnterpriseThumbnailDrawable; + boolean mPauseSchedulePendingForPip = false; + private void updateEnterpriseThumbnailDrawable(Context context) { DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class); mEnterpriseThumbnailDrawable = dpm.getResources().getDrawable( diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 12714ed51c73..14927f9eee7e 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -3583,7 +3583,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { null /* launchIntoPipHostActivity */, "enterPictureInPictureMode", transition); // Continue the pausing process after entering pip. - if (r.isState(PAUSING)) { + if (r.isState(PAUSING) && r.mPauseSchedulePendingForPip) { r.getTask().schedulePauseActivity(r, false /* userLeaving */, false /* pauseImmediately */, true /* autoEnteringPip */, "auto-pip"); } diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index 1a3e7d195859..ab979ad8340a 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -1619,6 +1619,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { if (prev.attachedToProcess()) { if (shouldAutoPip) { + prev.mPauseSchedulePendingForPip = true; boolean didAutoPip = mAtmService.enterPictureInPictureMode( prev, prev.pictureInPictureArgs, false /* fromClient */); ProtoLog.d(WM_DEBUG_STATES, "Auto-PIP allowed, entering PIP mode " @@ -1682,6 +1683,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { boolean pauseImmediately, boolean autoEnteringPip, String reason) { ProtoLog.v(WM_DEBUG_STATES, "Enqueueing pending pause: %s", prev); try { + prev.mPauseSchedulePendingForPip = false; EventLogTags.writeWmPauseActivity(prev.mUserId, System.identityHashCode(prev), prev.shortComponentName, "userLeaving=" + userLeaving, reason); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 7cccc5dd06c0..5ad5001cb121 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -17,6 +17,7 @@ package android.telephony; import android.Manifest; +import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -53,6 +54,7 @@ import com.android.telephony.Rlog; import java.util.List; import java.util.Objects; +import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; /** @@ -9726,4 +9728,85 @@ public class CarrierConfigManager { configs.putPersistableBundle(key, (PersistableBundle) value); } } + + /** + * Listener interface to get a notification when the carrier configurations have changed. + * + * Use this listener to receive timely updates when the carrier configuration changes. System + * components should prefer this listener over {@link #ACTION_CARRIER_CONFIG_CHANGED} + * whenever possible. + * + * To register the listener, call + * {@link #registerCarrierConfigChangeListener(Executor, CarrierConfigChangeListener)}. + * To unregister, call + * {@link #unregisterCarrierConfigChangeListener(CarrierConfigChangeListener)}. + * + * Note that on registration, registrants will NOT receive a notification on last carrier config + * change. Only carrier configs change AFTER the registration will be sent to registrants. And + * unlike {@link #ACTION_CARRIER_CONFIG_CHANGED}, notification wouldn't send when the device is + * unlocked. Registrants only receive the notification when there has been real carrier config + * changes. + * + * @see #registerCarrierConfigChangeListener(Executor, CarrierConfigChangeListener) + * @see #unregisterCarrierConfigChangeListener(CarrierConfigChangeListener) + * @see #ACTION_CARRIER_CONFIG_CHANGED + * @see #getConfig(String...) + * @see #getConfigForSubId(int, String...) + */ + public interface CarrierConfigChangeListener { + /** + * Called when carrier configurations have changed. + * + * @param logicalSlotIndex The logical SIM slot index on which to monitor and get + * notification. It is guaranteed to be valid. + * @param subscriptionId The subscription on the SIM slot. May be + * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}. + * @param carrierId The optional carrier Id, may be + * {@link TelephonyManager#UNKNOWN_CARRIER_ID}. + * See {@link TelephonyManager#getSimCarrierId()}. + * @param specificCarrierId The optional fine-grained carrierId, may be {@link + * TelephonyManager#UNKNOWN_CARRIER_ID}. A specific carrierId may + * be different from the carrierId above in a MVNO scenario. See + * detail in {@link TelephonyManager#getSimSpecificCarrierId()}. + */ + void onCarrierConfigChanged(int logicalSlotIndex, int subscriptionId, int carrierId, + int specificCarrierId); + } + + /** + * Register a {@link CarrierConfigChangeListener} to get a notification when carrier + * configurations have changed. + * + * @param executor The executor on which the listener will be called. + * @param listener The CarrierConfigChangeListener called when carrier configs has changed. + */ + public void registerCarrierConfigChangeListener(@NonNull @CallbackExecutor Executor executor, + @NonNull CarrierConfigChangeListener listener) { + Objects.requireNonNull(executor, "Executor should be non-null."); + Objects.requireNonNull(listener, "Listener should be non-null."); + + TelephonyRegistryManager trm = mContext.getSystemService(TelephonyRegistryManager.class); + if (trm == null) { + throw new IllegalStateException("Telephony registry service is null"); + } + trm.addCarrierConfigChangedListener(executor, listener); + } + + /** + * Unregister the {@link CarrierConfigChangeListener} to stop notification on carrier + * configurations change. + * + * @param listener The CarrierConfigChangeListener which was registered with method + * {@link #registerCarrierConfigChangeListener(Executor, CarrierConfigChangeListener)}. + */ + public void unregisterCarrierConfigChangeListener( + @NonNull CarrierConfigChangeListener listener) { + Objects.requireNonNull(listener, "Listener should be non-null."); + + TelephonyRegistryManager trm = mContext.getSystemService(TelephonyRegistryManager.class); + if (trm == null) { + throw new IllegalStateException("Telephony registry service is null"); + } + trm.removeCarrierConfigChangedListener(listener); + } } diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java index e055f637b72c..2ddc98aca90c 100644 --- a/telephony/java/android/telephony/SubscriptionInfo.java +++ b/telephony/java/android/telephony/SubscriptionInfo.java @@ -649,6 +649,15 @@ public class SubscriptionInfo implements Parcelable { } /** + * @return {@code true} if the subscription is from the actively used SIM. + * + * @hide + */ + public boolean isActive() { + return mSimSlotIndex >= 0 || mType == SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM; + } + + /** * Used in scenarios where different subscriptions are bundled as a group. * It's typically a primary and an opportunistic subscription. (see {@link #isOpportunistic()}) * Such that those subscriptions will have some affiliated behaviors such as opportunistic |