diff options
58 files changed, 1979 insertions, 698 deletions
diff --git a/api/current.txt b/api/current.txt index 5ffb05541621..80a7bb49b1e0 100644 --- a/api/current.txt +++ b/api/current.txt @@ -29160,7 +29160,7 @@ package android.net { method @NonNull public android.net.NetworkCapabilities setLinkDownstreamBandwidthKbps(int); method @NonNull public android.net.NetworkCapabilities setLinkUpstreamBandwidthKbps(int); method @NonNull public android.net.NetworkCapabilities setNetworkSpecifier(@NonNull android.net.NetworkSpecifier); - method public void setOwnerUid(int); + method @NonNull public android.net.NetworkCapabilities setOwnerUid(int); method @NonNull public android.net.NetworkCapabilities setSignalStrength(int); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkCapabilities> CREATOR; @@ -43685,7 +43685,7 @@ package android.telecom { field public static final int DIRECTION_INCOMING = 0; // 0x0 field public static final int DIRECTION_OUTGOING = 1; // 0x1 field public static final int DIRECTION_UNKNOWN = -1; // 0xffffffff - field public static final int PROPERTY_ASSISTED_DIALING_USED = 512; // 0x200 + field public static final int PROPERTY_ASSISTED_DIALING = 512; // 0x200 field public static final int PROPERTY_CONFERENCE = 1; // 0x1 field public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 4; // 0x4 field public static final int PROPERTY_ENTERPRISE_CALL = 32; // 0x20 @@ -43774,7 +43774,8 @@ package android.telecom { method public final java.util.List<android.telecom.Connection> getConferenceableConnections(); method public final int getConnectionCapabilities(); method public final int getConnectionProperties(); - method public final long getConnectionTime(); + method public final long getConnectionStartElapsedRealtimeMillis(); + method @IntRange(from=0) public final long getConnectionTime(); method public final java.util.List<android.telecom.Connection> getConnections(); method public final android.telecom.DisconnectCause getDisconnectCause(); method public final android.os.Bundle getExtras(); @@ -43804,8 +43805,9 @@ package android.telecom { method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>); method public final void setConnectionCapabilities(int); method public final void setConnectionProperties(int); - method public final void setConnectionStartElapsedRealTime(long); - method public final void setConnectionTime(long); + method @Deprecated public final void setConnectionStartElapsedRealTime(long); + method public final void setConnectionStartElapsedRealtimeMillis(long); + method public final void setConnectionTime(@IntRange(from=0) long); method public final void setDialing(); method public final void setDisconnected(android.telecom.DisconnectCause); method public final void setExtras(@Nullable android.os.Bundle); @@ -43965,7 +43967,7 @@ package android.telecom { field public static final String EXTRA_IS_RTT_AUDIO_PRESENT = "android.telecom.extra.IS_RTT_AUDIO_PRESENT"; field public static final String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER"; field public static final String EXTRA_SIP_INVITE = "android.telecom.extra.SIP_INVITE"; - field public static final int PROPERTY_ASSISTED_DIALING_USED = 512; // 0x200 + field public static final int PROPERTY_ASSISTED_DIALING = 512; // 0x200 field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20 field public static final int PROPERTY_HIGH_DEF_AUDIO = 4; // 0x4 field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10 diff --git a/api/system-current.txt b/api/system-current.txt index c978ef8b700c..202d51ff9d92 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -4649,7 +4649,9 @@ package android.net { method @Nullable public String getSSID(); method @NonNull public int[] getTransportTypes(); method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities); - method public void setAdministratorUids(@NonNull java.util.List<java.lang.Integer>); + method @NonNull public android.net.NetworkCapabilities setAdministratorUids(@NonNull java.util.List<java.lang.Integer>); + method @NonNull public android.net.NetworkCapabilities setRequestorPackageName(@NonNull String); + method @NonNull public android.net.NetworkCapabilities setRequestorUid(int); method @NonNull public android.net.NetworkCapabilities setSSID(@Nullable String); method @NonNull public android.net.NetworkCapabilities setTransportInfo(@NonNull android.net.TransportInfo); field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16 @@ -4701,6 +4703,8 @@ package android.net { } public class NetworkRequest implements android.os.Parcelable { + method @Nullable public String getRequestorPackageName(); + method public int getRequestorUid(); method public boolean satisfiedBy(@Nullable android.net.NetworkCapabilities); } @@ -4735,7 +4739,6 @@ package android.net { } public abstract class NetworkSpecifier { - method public void assertValidFromUid(int); method @Nullable public android.net.NetworkSpecifier redact(); method public abstract boolean satisfiedBy(@Nullable android.net.NetworkSpecifier); } @@ -7176,6 +7179,7 @@ package android.provider { field public static final String ACTION_REQUEST_ENABLE_CONTENT_CAPTURE = "android.settings.REQUEST_ENABLE_CONTENT_CAPTURE"; field public static final String ACTION_SHOW_ADMIN_SUPPORT_DETAILS = "android.settings.SHOW_ADMIN_SUPPORT_DETAILS"; field public static final String ACTION_TETHER_PROVISIONING_UI = "android.settings.TETHER_PROVISIONING_UI"; + field public static final String ACTION_TETHER_SETTINGS = "android.settings.TETHER_SETTINGS"; } public static final class Settings.Global extends android.provider.Settings.NameValueTable { @@ -8253,27 +8257,26 @@ package android.telecom { public abstract class Conference extends android.telecom.Conferenceable { method @Deprecated public final android.telecom.AudioState getAudioState(); method @Deprecated public final long getConnectTimeMillis(); - method public final long getConnectionStartElapsedRealTime(); method public android.telecom.Connection getPrimaryConnection(); method @NonNull public final String getTelecomCallId(); method @Deprecated public void onAudioStateChanged(android.telecom.AudioState); - method public final void setAddress(@NonNull android.net.Uri, int); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setAddress(@NonNull android.net.Uri, int); method public final void setCallerDisplayName(@NonNull String, int); - method public void setConferenceState(boolean); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setConferenceState(boolean); method @Deprecated public final void setConnectTimeMillis(long); } public abstract class Connection extends android.telecom.Conferenceable { method @Deprecated public final android.telecom.AudioState getAudioState(); - method public final long getConnectElapsedTimeMillis(); - method public final long getConnectTimeMillis(); + method @IntRange(from=0) public final long getConnectTimeMillis(); + method public final long getConnectionStartElapsedRealtimeMillis(); method @Nullable public android.telecom.PhoneAccountHandle getPhoneAccountHandle(); method @Nullable public final String getTelecomCallId(); method @Deprecated public void onAudioStateChanged(android.telecom.AudioState); method public final void resetConnectionTime(); method public void setCallDirection(int); - method public final void setConnectTimeMillis(long); - method public final void setConnectionStartElapsedRealTime(long); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setConnectTimeMillis(@IntRange(from=0) long); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setConnectionStartElapsedRealtimeMillis(long); method public void setPhoneAccountHandle(@NonNull android.telecom.PhoneAccountHandle); method public void setTelecomCallId(@NonNull String); field public static final int CAPABILITY_CONFERENCE_HAS_NO_CHILDREN = 2097152; // 0x200000 @@ -8425,7 +8428,7 @@ package android.telecom { } public static class PhoneAccount.Builder { - method @NonNull public android.telecom.PhoneAccount.Builder setGroupId(@NonNull String); + method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telecom.PhoneAccount.Builder setGroupId(@NonNull String); } public class PhoneAccountSuggestionService extends android.app.Service { @@ -8500,7 +8503,7 @@ package android.telecom { method public int getCallState(); method public android.telecom.PhoneAccountHandle getConnectionManager(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCurrentTtyMode(); - method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDefaultDialerPackage(int); + method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDefaultDialerPackage(@NonNull android.os.UserHandle); method @Deprecated public android.content.ComponentName getDefaultPhoneApp(); method public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsForPackage(); method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsSupportingScheme(String); @@ -9660,6 +9663,7 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin(int); method public String getCdmaPrlVersion(); + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCdmaRoamingMode(); method public int getCurrentPhoneType(); method public int getCurrentPhoneType(int); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getDataActivationState(); @@ -9737,6 +9741,8 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setCallWaitingStatus(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCarrierDataEnabled(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setCarrierRestrictionRules(@NonNull android.telephony.CarrierRestrictionRules); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setCdmaRoamingMode(int); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setCdmaSubscriptionMode(int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataActivationState(int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setDataAllowedDuringVoiceCall(boolean); method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean); @@ -9784,6 +9790,9 @@ package android.telephony { field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1 field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0 field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff + field public static final int CDMA_SUBSCRIPTION_NV = 1; // 0x1 + field public static final int CDMA_SUBSCRIPTION_RUIM_SIM = 0; // 0x0 + field public static final int CDMA_SUBSCRIPTION_UNKNOWN = -1; // 0xffffffff field public static final int CHANGE_ICC_LOCK_SUCCESS = 2147483647; // 0x7fffffff field public static final String EXTRA_ANOMALY_DESCRIPTION = "android.telephony.extra.ANOMALY_DESCRIPTION"; field public static final String EXTRA_ANOMALY_ID = "android.telephony.extra.ANOMALY_ID"; diff --git a/api/test-current.txt b/api/test-current.txt index 308afdc7dcee..b3e80a98a2e2 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -3005,23 +3005,22 @@ package android.telecom { } public abstract class Conference extends android.telecom.Conferenceable { - method public final long getConnectionStartElapsedRealTime(); method public android.telecom.Connection getPrimaryConnection(); method @NonNull public final String getTelecomCallId(); - method public final void setAddress(@NonNull android.net.Uri, int); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setAddress(@NonNull android.net.Uri, int); method public final void setCallerDisplayName(@NonNull String, int); - method public void setConferenceState(boolean); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setConferenceState(boolean); } public abstract class Connection extends android.telecom.Conferenceable { - method public final long getConnectElapsedTimeMillis(); - method public final long getConnectTimeMillis(); + method @IntRange(from=0) public final long getConnectTimeMillis(); + method public final long getConnectionStartElapsedRealtimeMillis(); method @Nullable public android.telecom.PhoneAccountHandle getPhoneAccountHandle(); method @Nullable public final String getTelecomCallId(); method public final void resetConnectionTime(); method public void setCallDirection(int); - method public final void setConnectTimeMillis(long); - method public final void setConnectionStartElapsedRealTime(long); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setConnectTimeMillis(@IntRange(from=0) long); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setConnectionStartElapsedRealtimeMillis(long); method public void setPhoneAccountHandle(@NonNull android.telecom.PhoneAccountHandle); method public void setTelecomCallId(@NonNull String); field public static final int CAPABILITY_CONFERENCE_HAS_NO_CHILDREN = 2097152; // 0x200000 @@ -3053,7 +3052,7 @@ package android.telecom { } public static class PhoneAccount.Builder { - method @NonNull public android.telecom.PhoneAccount.Builder setGroupId(@NonNull String); + method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telecom.PhoneAccount.Builder setGroupId(@NonNull String); } public class PhoneAccountSuggestionService extends android.app.Service { @@ -3067,7 +3066,7 @@ package android.telecom { public class TelecomManager { method @NonNull @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public java.util.List<android.telecom.PhoneAccountHandle> getCallCapablePhoneAccounts(boolean); method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public int getCurrentTtyMode(); - method @Nullable @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getDefaultDialerPackage(int); + method @Nullable @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getDefaultDialerPackage(@NonNull android.os.UserHandle); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isInEmergencyCall(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUserSelectedOutgoingPhoneAccount(@Nullable android.telecom.PhoneAccountHandle); field public static final int TTY_MODE_FULL = 1; // 0x1 diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java index b9cda6c7c183..2eb9459af877 100644 --- a/core/java/android/app/role/RoleManager.java +++ b/core/java/android/app/role/RoleManager.java @@ -89,6 +89,7 @@ public final class RoleManager { * The name of the dialer role. * * @see Intent#ACTION_DIAL + * @see android.telecom.InCallService */ public static final String ROLE_DIALER = "android.app.role.DIALER"; diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java index 9c4a8f4fbe27..5b98188300c9 100644 --- a/core/java/android/app/usage/NetworkStatsManager.java +++ b/core/java/android/app/usage/NetworkStatsManager.java @@ -526,15 +526,17 @@ public class NetworkStatsManager { } /** - * Registers a custom provider of {@link android.net.NetworkStats} to combine the network - * statistics that cannot be seen by the kernel to system. To unregister, invoke - * {@link NetworkStatsProviderCallback#unregister()}. + * Registers a custom provider of {@link android.net.NetworkStats} to provide network statistics + * to the system. To unregister, invoke {@link NetworkStatsProviderCallback#unregister()}. + * Note that no de-duplication of statistics between providers is performed, so each provider + * must only report network traffic that is not being reported by any other provider. * - * @param tag a human readable identifier of the custom network stats provider. - * @param provider a custom implementation of {@link AbstractNetworkStatsProvider} that needs to - * be registered to the system. + * @param tag a human readable identifier of the custom network stats provider. This is only + * used for debugging. + * @param provider the subclass of {@link AbstractNetworkStatsProvider} that needs to be + * registered to the system. * @return a {@link NetworkStatsProviderCallback}, which can be used to report events to the - * system. + * system or unregister the provider. * @hide */ @SystemApi diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 212c716eb680..cb3140487f35 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -3746,6 +3746,7 @@ public class ConnectivityManager { checkCallbackNotNull(callback); Preconditions.checkArgument(action == REQUEST || need != null, "null NetworkCapabilities"); final NetworkRequest request; + final String callingPackageName = mContext.getOpPackageName(); try { synchronized(sCallbacks) { if (callback.networkRequest != null @@ -3757,10 +3758,11 @@ public class ConnectivityManager { Messenger messenger = new Messenger(handler); Binder binder = new Binder(); if (action == LISTEN) { - request = mService.listenForNetwork(need, messenger, binder); + request = mService.listenForNetwork( + need, messenger, binder, callingPackageName); } else { request = mService.requestNetwork( - need, messenger, timeoutMs, binder, legacyType); + need, messenger, timeoutMs, binder, legacyType, callingPackageName); } if (request != null) { sCallbacks.put(request, callback); @@ -4033,8 +4035,10 @@ public class ConnectivityManager { @NonNull PendingIntent operation) { printStackTrace(); checkPendingIntentNotNull(operation); + final String callingPackageName = mContext.getOpPackageName(); try { - mService.pendingRequestForNetwork(request.networkCapabilities, operation); + mService.pendingRequestForNetwork( + request.networkCapabilities, operation, callingPackageName); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (ServiceSpecificException e) { @@ -4146,8 +4150,10 @@ public class ConnectivityManager { @NonNull PendingIntent operation) { printStackTrace(); checkPendingIntentNotNull(operation); + final String callingPackageName = mContext.getOpPackageName(); try { - mService.pendingListenForNetwork(request.networkCapabilities, operation); + mService.pendingListenForNetwork( + request.networkCapabilities, operation, callingPackageName); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (ServiceSpecificException e) { diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index c871c456dc66..3a55461a77d2 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -166,18 +166,19 @@ interface IConnectivityManager in int factorySerialNumber); NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities, - in Messenger messenger, int timeoutSec, in IBinder binder, int legacy); + in Messenger messenger, int timeoutSec, in IBinder binder, int legacy, + String callingPackageName); NetworkRequest pendingRequestForNetwork(in NetworkCapabilities networkCapabilities, - in PendingIntent operation); + in PendingIntent operation, String callingPackageName); void releasePendingNetworkRequest(in PendingIntent operation); NetworkRequest listenForNetwork(in NetworkCapabilities networkCapabilities, - in Messenger messenger, in IBinder binder); + in Messenger messenger, in IBinder binder, String callingPackageName); void pendingListenForNetwork(in NetworkCapabilities networkCapabilities, - in PendingIntent operation); + in PendingIntent operation, String callingPackageName); void releaseNetworkRequest(in NetworkRequest networkRequest); diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java index 09ec6c35fcb9..d83715c692f7 100644 --- a/core/java/android/net/IpSecManager.java +++ b/core/java/android/net/IpSecManager.java @@ -51,7 +51,7 @@ import java.net.Socket; * * <p>Note that not all aspects of IPsec are permitted by this API. Applications may create * transport mode security associations and apply them to individual sockets. Applications looking - * to create a VPN should use {@link VpnService}. + * to create an IPsec VPN should use {@link VpnManager} and {@link Ikev2VpnProfile}. * * @see <a href="https://tools.ietf.org/html/rfc4301">RFC 4301, Security Architecture for the * Internet Protocol</a> diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index 38f7390abffd..ef4a9e5f3b5d 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -27,6 +27,7 @@ import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.os.Process; +import android.text.TextUtils; import android.util.ArraySet; import android.util.proto.ProtoOutputStream; @@ -63,6 +64,16 @@ public final class NetworkCapabilities implements Parcelable { // Set to true when private DNS is broken. private boolean mPrivateDnsBroken; + /** + * Uid of the app making the request. + */ + private int mRequestorUid; + + /** + * Package name of the app making the request. + */ + private String mRequestorPackageName; + public NetworkCapabilities() { clearAll(); mNetworkCapabilities = DEFAULT_CAPABILITIES; @@ -89,6 +100,8 @@ public final class NetworkCapabilities implements Parcelable { mOwnerUid = Process.INVALID_UID; mSSID = null; mPrivateDnsBroken = false; + mRequestorUid = Process.INVALID_UID; + mRequestorPackageName = null; } /** @@ -109,6 +122,8 @@ public final class NetworkCapabilities implements Parcelable { mUnwantedNetworkCapabilities = nc.mUnwantedNetworkCapabilities; mSSID = nc.mSSID; mPrivateDnsBroken = nc.mPrivateDnsBroken; + mRequestorUid = nc.mRequestorUid; + mRequestorPackageName = nc.mRequestorPackageName; } /** @@ -810,7 +825,7 @@ public final class NetworkCapabilities implements Parcelable { } /** - * UID of the app that owns this network, or INVALID_UID if none/unknown. + * UID of the app that owns this network, or Process#INVALID_UID if none/unknown. * * <p>This field keeps track of the UID of the app that created this network and is in charge of * its lifecycle. This could be the UID of apps such as the Wifi network suggestor, the running @@ -821,8 +836,9 @@ public final class NetworkCapabilities implements Parcelable { /** * Set the UID of the owner app. */ - public void setOwnerUid(final int uid) { + public @NonNull NetworkCapabilities setOwnerUid(final int uid) { mOwnerUid = uid; + return this; } /** @@ -865,9 +881,11 @@ public final class NetworkCapabilities implements Parcelable { * @hide */ @SystemApi - public void setAdministratorUids(@NonNull final List<Integer> administratorUids) { + public @NonNull NetworkCapabilities setAdministratorUids( + @NonNull final List<Integer> administratorUids) { mAdministratorUids.clear(); mAdministratorUids.addAll(administratorUids); + return this; } /** @@ -1385,6 +1403,7 @@ public final class NetworkCapabilities implements Parcelable { combineSignalStrength(nc); combineUids(nc); combineSSIDs(nc); + combineRequestor(nc); } /** @@ -1404,7 +1423,8 @@ public final class NetworkCapabilities implements Parcelable { && satisfiedBySpecifier(nc) && (onlyImmutable || satisfiedBySignalStrength(nc)) && (onlyImmutable || satisfiedByUids(nc)) - && (onlyImmutable || satisfiedBySSID(nc))); + && (onlyImmutable || satisfiedBySSID(nc))) + && (onlyImmutable || satisfiedByRequestor(nc)); } /** @@ -1488,7 +1508,7 @@ public final class NetworkCapabilities implements Parcelable { public boolean equals(@Nullable Object obj) { if (obj == null || (obj instanceof NetworkCapabilities == false)) return false; NetworkCapabilities that = (NetworkCapabilities) obj; - return (equalsNetCapabilities(that) + return equalsNetCapabilities(that) && equalsTransportTypes(that) && equalsLinkBandwidths(that) && equalsSignalStrength(that) @@ -1496,7 +1516,8 @@ public final class NetworkCapabilities implements Parcelable { && equalsTransportInfo(that) && equalsUids(that) && equalsSSID(that) - && equalsPrivateDnsBroken(that)); + && equalsPrivateDnsBroken(that) + && equalsRequestor(that); } @Override @@ -1514,7 +1535,9 @@ public final class NetworkCapabilities implements Parcelable { + Objects.hashCode(mUids) * 31 + Objects.hashCode(mSSID) * 37 + Objects.hashCode(mTransportInfo) * 41 - + Objects.hashCode(mPrivateDnsBroken) * 43; + + Objects.hashCode(mPrivateDnsBroken) * 43 + + Objects.hashCode(mRequestorUid) * 47 + + Objects.hashCode(mRequestorPackageName) * 53; } @Override @@ -1537,6 +1560,8 @@ public final class NetworkCapabilities implements Parcelable { dest.writeBoolean(mPrivateDnsBroken); dest.writeList(mAdministratorUids); dest.writeInt(mOwnerUid); + dest.writeInt(mRequestorUid); + dest.writeString(mRequestorPackageName); } public static final @android.annotation.NonNull Creator<NetworkCapabilities> CREATOR = @@ -1559,6 +1584,8 @@ public final class NetworkCapabilities implements Parcelable { netCap.mPrivateDnsBroken = in.readBoolean(); netCap.setAdministratorUids(in.readArrayList(null)); netCap.mOwnerUid = in.readInt(); + netCap.mRequestorUid = in.readInt(); + netCap.mRequestorPackageName = in.readString(); return netCap; } @Override @@ -1624,6 +1651,9 @@ public final class NetworkCapabilities implements Parcelable { sb.append(" Private DNS is broken"); } + sb.append(" RequestorUid: ").append(mRequestorUid); + sb.append(" RequestorPackageName: ").append(mRequestorPackageName); + sb.append("]"); return sb.toString(); } @@ -1632,6 +1662,7 @@ public final class NetworkCapabilities implements Parcelable { private interface NameOf { String nameOf(int value); } + /** * @hide */ @@ -1799,4 +1830,120 @@ public final class NetworkCapabilities implements Parcelable { private boolean equalsPrivateDnsBroken(NetworkCapabilities nc) { return mPrivateDnsBroken == nc.mPrivateDnsBroken; } + + /** + * Set the uid of the app making the request. + * + * Note: This works only for {@link NetworkAgent} instances. Any capabilities passed in + * via the public {@link ConnectivityManager} API's will have this field overwritten. + * + * @param uid UID of the app. + * @hide + */ + @SystemApi + public @NonNull NetworkCapabilities setRequestorUid(int uid) { + mRequestorUid = uid; + return this; + } + + /** + * @return the uid of the app making the request. + * + * Note: This could return {@link Process#INVALID_UID} if the {@link NetworkRequest} + * object was not obtained from {@link ConnectivityManager}. + * @hide + */ + public int getRequestorUid() { + return mRequestorUid; + } + + /** + * Set the package name of the app making the request. + * + * Note: This works only for {@link NetworkAgent} instances. Any capabilities passed in + * via the public {@link ConnectivityManager} API's will have this field overwritten. + * + * @param packageName package name of the app. + * @hide + */ + @SystemApi + public @NonNull NetworkCapabilities setRequestorPackageName(@NonNull String packageName) { + mRequestorPackageName = packageName; + return this; + } + + /** + * @return the package name of the app making the request. + * + * Note: This could return {@code null} if the {@link NetworkRequest} object was not obtained + * from {@link ConnectivityManager}. + * @hide + */ + @Nullable + public String getRequestorPackageName() { + return mRequestorPackageName; + } + + /** + * Set the uid and package name of the app making the request. + * + * Note: This is intended to be only invoked from within connectivitiy service. + * + * @param uid UID of the app. + * @param packageName package name of the app. + * @hide + */ + public @NonNull NetworkCapabilities setRequestorUidAndPackageName( + int uid, @NonNull String packageName) { + return setRequestorUid(uid).setRequestorPackageName(packageName); + } + + /** + * Test whether the passed NetworkCapabilities satisfies the requestor restrictions of this + * capabilities. + * + * This method is called on the NetworkCapabilities embedded in a request with the + * capabilities of an available network. If the available network, sets a specific + * requestor (by uid and optionally package name), then this will only match a request from the + * same app. If either of the capabilities have an unset uid or package name, then it matches + * everything. + * <p> + * nc is assumed nonnull. Else, NPE. + */ + private boolean satisfiedByRequestor(NetworkCapabilities nc) { + // No uid set, matches everything. + if (mRequestorUid == Process.INVALID_UID || nc.mRequestorUid == Process.INVALID_UID) { + return true; + } + // uids don't match. + if (mRequestorUid != nc.mRequestorUid) return false; + // No package names set, matches everything + if (null == nc.mRequestorPackageName || null == mRequestorPackageName) return true; + // check for package name match. + return TextUtils.equals(mRequestorPackageName, nc.mRequestorPackageName); + } + + /** + * Combine requestor info of the capabilities. + * <p> + * This is only legal if either the requestor info of this object is reset, or both info are + * equal. + * nc is assumed nonnull. + */ + private void combineRequestor(@NonNull NetworkCapabilities nc) { + if (mRequestorUid != Process.INVALID_UID && mRequestorUid != nc.mOwnerUid) { + throw new IllegalStateException("Can't combine two uids"); + } + if (mRequestorPackageName != null + && !mRequestorPackageName.equals(nc.mRequestorPackageName)) { + throw new IllegalStateException("Can't combine two package names"); + } + setRequestorUid(nc.mRequestorUid); + setRequestorPackageName(nc.mRequestorPackageName); + } + + private boolean equalsRequestor(NetworkCapabilities nc) { + return mRequestorUid == nc.mRequestorUid + && TextUtils.equals(mRequestorPackageName, nc.mRequestorPackageName); + } } diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java index ee4379a85b6b..b0bf64ecec56 100644 --- a/core/java/android/net/NetworkRequest.java +++ b/core/java/android/net/NetworkRequest.java @@ -380,6 +380,7 @@ public class NetworkRequest implements Parcelable { dest.writeInt(requestId); dest.writeString(type.name()); } + public static final @android.annotation.NonNull Creator<NetworkRequest> CREATOR = new Creator<NetworkRequest>() { public NetworkRequest createFromParcel(Parcel in) { @@ -494,6 +495,31 @@ public class NetworkRequest implements Parcelable { return networkCapabilities.getNetworkSpecifier(); } + /** + * @return the uid of the app making the request. + * + * Note: This could return {@link Process#INVALID_UID} if the {@link NetworkRequest} object was + * not obtained from {@link ConnectivityManager}. + * @hide + */ + @SystemApi + public int getRequestorUid() { + return networkCapabilities.getRequestorUid(); + } + + /** + * @return the package name of the app making the request. + * + * Note: This could return {@code null} if the {@link NetworkRequest} object was not obtained + * from {@link ConnectivityManager}. + * @hide + */ + @SystemApi + @Nullable + public String getRequestorPackageName() { + return networkCapabilities.getRequestorPackageName(); + } + public String toString() { return "NetworkRequest [ " + type + " id=" + requestId + (legacyType != ConnectivityManager.TYPE_NONE ? ", legacyType=" + legacyType : "") + diff --git a/core/java/android/net/NetworkSpecifier.java b/core/java/android/net/NetworkSpecifier.java index cf31d217c967..2dd0c4e207fe 100644 --- a/core/java/android/net/NetworkSpecifier.java +++ b/core/java/android/net/NetworkSpecifier.java @@ -39,23 +39,6 @@ public abstract class NetworkSpecifier { /** * Optional method which can be overridden by concrete implementations of NetworkSpecifier to - * check a self-reported UID. A concrete implementation may contain a UID which would be self- - * reported by the caller (since NetworkSpecifier implementations should be non-mutable). This - * function is called by ConnectivityService and is passed the actual UID of the caller - - * allowing the verification of the self-reported UID. In cases of mismatch the implementation - * should throw a SecurityException. - * - * @param requestorUid The UID of the requestor as obtained from its binder. - * - * @hide - */ - @SystemApi - public void assertValidFromUid(int requestorUid) { - // empty - } - - /** - * Optional method which can be overridden by concrete implementations of NetworkSpecifier to * perform any redaction of information from the NetworkSpecifier, e.g. if it contains * sensitive information. The default implementation simply returns the object itself - i.e. * no information is redacted. A concrete implementation may return a modified (copy) of the diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java index 25584f156084..c5c26b0d6482 100644 --- a/core/java/android/os/Trace.java +++ b/core/java/android/os/Trace.java @@ -175,11 +175,7 @@ public final class Trace { } /** - * Set whether tracing is enabled in this process. Tracing is disabled shortly after Zygote - * initializes and re-enabled after processes fork from Zygote. This is done because Zygote - * has no way to be notified about changes to the tracing tags, and if Zygote ever reads and - * caches the tracing tags, forked processes will inherit those stale tags. - * + * Set whether tracing is enabled in this process. * @hide */ public static void setTracingEnabled(boolean enabled, int debugFlags) { diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index b22db2e24624..3842def8751d 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -1793,6 +1793,19 @@ public final class Settings { public static final String ACTION_MANAGE_DOMAIN_URLS = "android.settings.MANAGE_DOMAIN_URLS"; /** + * Activity Action: Show screen that let user select enable (or disable) tethering. + * <p> + * Input: Nothing. + * <p> + * Output: Nothing + * + * @hide + */ + @SystemApi + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_TETHER_SETTINGS = "android.settings.TETHER_SETTINGS"; + + /** * Broadcast to trigger notification of asking user to enable MMS. * Need to specify {@link #EXTRA_ENABLE_MMS_DATA_REQUEST_REASON} and {@link #EXTRA_SUB_ID}. * diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index c1c74dcfb9e6..72eb32a05da9 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -24,7 +24,6 @@ import android.content.pm.ApplicationInfo; import android.net.Credentials; import android.net.LocalServerSocket; import android.net.LocalSocket; -import android.os.Build; import android.os.FactoryTest; import android.os.IVold; import android.os.Process; @@ -254,18 +253,13 @@ public final class Zygote { */ public static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose, - int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir, - int targetSdkVersion) { + int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir) { ZygoteHooks.preFork(); int pid = nativeForkAndSpecialize( uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose, fdsToIgnore, startChildZygote, instructionSet, appDataDir); - // Enable tracing as soon as possible for the child process. if (pid == 0) { - Zygote.disableExecuteOnly(targetSdkVersion); - Trace.setTracingEnabled(true, runtimeFlags); - // Note that this event ends at the end of handleChildProc, Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork"); } @@ -309,9 +303,6 @@ public final class Zygote { nativeSpecializeAppProcess(uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, startChildZygote, instructionSet, appDataDir); - // Enable tracing as soon as possible for the child process. - Trace.setTracingEnabled(true, runtimeFlags); - // Note that this event ends at the end of handleChildProc. Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork"); @@ -367,11 +358,6 @@ public final class Zygote { uid, gid, gids, runtimeFlags, rlimits, permittedCapabilities, effectiveCapabilities); - // Enable tracing as soon as we enter the system_server. - if (pid == 0) { - Trace.setTracingEnabled(true, runtimeFlags); - } - // Set the Java Language thread priority to the default value for new apps. Thread.currentThread().setPriority(Thread.NORM_PRIORITY); @@ -659,8 +645,6 @@ public final class Zygote { args.mSeInfo, args.mNiceName, args.mStartChildZygote, args.mInstructionSet, args.mAppDataDir); - disableExecuteOnly(args.mTargetSdkVersion); - Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); return ZygoteInit.zygoteInit(args.mTargetSdkVersion, @@ -740,17 +724,6 @@ public final class Zygote { } /** - * Mark execute-only segments of libraries read+execute for apps with targetSdkVersion<Q. - */ - protected static void disableExecuteOnly(int targetSdkVersion) { - if ((targetSdkVersion < Build.VERSION_CODES.Q) && !nativeDisableExecuteOnly()) { - Log.e("Zygote", "Failed to set libraries to read+execute."); - } - } - - private static native boolean nativeDisableExecuteOnly(); - - /** * @return Raw file descriptors for the read-end of USAP reporting pipes. */ protected static int[] getUsapPipeFDs() { diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java index 2666d5278a90..24ea59ae6ac0 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -257,7 +257,7 @@ class ZygoteConnection { pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid, parsedArgs.mGids, parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo, parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote, - parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mTargetSdkVersion); + parsedArgs.mInstructionSet, parsedArgs.mAppDataDir); try { if (pid == 0) { diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 348262e1a9d3..decc92cfc0c4 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -647,17 +647,18 @@ public class ZygoteInit { String classPathForElement = ""; boolean compiledSomething = false; for (String classPathElement : classPathElements) { - // System server is fully AOTed and never profiled - // for profile guided compilation. + // We default to the verify filter because the compilation will happen on /data and + // system server cannot load executable code outside /system. String systemServerFilter = SystemProperties.get( - "dalvik.vm.systemservercompilerfilter", "speed"); + "dalvik.vm.systemservercompilerfilter", "verify"); + String classLoaderContext = + getSystemServerClassLoaderContext(classPathForElement); int dexoptNeeded; try { dexoptNeeded = DexFile.getDexOptNeeded( classPathElement, instructionSet, systemServerFilter, - null /* classLoaderContext */, false /* newProfile */, - false /* downgrade */); + classLoaderContext, false /* newProfile */, false /* downgrade */); } catch (FileNotFoundException ignored) { // Do not add to the classpath. Log.w(TAG, "Missing classpath element for system server: " + classPathElement); @@ -678,8 +679,6 @@ public class ZygoteInit { final String compilerFilter = systemServerFilter; final String uuid = StorageManager.UUID_PRIVATE_INTERNAL; final String seInfo = null; - final String classLoaderContext = - getSystemServerClassLoaderContext(classPathForElement); final int targetSdkVersion = 0; // SystemServer targets the system's SDK version try { installd.dexopt(classPathElement, Process.SYSTEM_UID, packageName, @@ -908,10 +907,6 @@ public class ZygoteInit { bootTimingsTraceLog.traceEnd(); // PostZygoteInitGC bootTimingsTraceLog.traceEnd(); // ZygoteInit - // Disable tracing so that forked processes do not inherit stale tracing tags from - // Zygote. - Trace.setTracingEnabled(false, 0); - Zygote.initNativeState(isPrimaryZygote); diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 7a93d8db0931..7e4a16dc6dcc 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -46,7 +46,6 @@ #include <fcntl.h> #include <grp.h> #include <inttypes.h> -#include <link.h> #include <malloc.h> #include <mntent.h> #include <paths.h> @@ -55,7 +54,6 @@ #include <sys/capability.h> #include <sys/cdefs.h> #include <sys/eventfd.h> -#include <sys/mman.h> #include <sys/personality.h> #include <sys/prctl.h> #include <sys/resource.h> @@ -72,10 +70,8 @@ #include <android-base/properties.h> #include <android-base/file.h> #include <android-base/stringprintf.h> -#include <android-base/strings.h> #include <android-base/unique_fd.h> #include <bionic/malloc.h> -#include <bionic/page.h> #include <cutils/fs.h> #include <cutils/multiuser.h> #include <private/android_filesystem_config.h> @@ -1783,31 +1779,6 @@ static void com_android_internal_os_Zygote_nativeEmptyUsapPool(JNIEnv* env, jcla } } -static int disable_execute_only(struct dl_phdr_info *info, size_t size, void *data) { - // Search for any execute-only segments and mark them read+execute. - for (int i = 0; i < info->dlpi_phnum; i++) { - const auto& phdr = info->dlpi_phdr[i]; - if ((phdr.p_type == PT_LOAD) && (phdr.p_flags == PF_X)) { - auto addr = reinterpret_cast<void*>(info->dlpi_addr + PAGE_START(phdr.p_vaddr)); - size_t len = PAGE_OFFSET(phdr.p_vaddr) + phdr.p_memsz; - if (mprotect(addr, len, PROT_READ | PROT_EXEC) == -1) { - ALOGE("mprotect(%p, %zu, PROT_READ | PROT_EXEC) failed: %m", addr, len); - return -1; - } - } - } - // Return non-zero to exit dl_iterate_phdr. - return 0; -} - -/** - * @param env Managed runtime environment - * @return True if disable was successful. - */ -static jboolean com_android_internal_os_Zygote_nativeDisableExecuteOnly(JNIEnv* env, jclass) { - return dl_iterate_phdr(disable_execute_only, nullptr) == 0; -} - static void com_android_internal_os_Zygote_nativeBlockSigTerm(JNIEnv* env, jclass) { auto fail_fn = std::bind(ZygoteFailure, env, "usap", nullptr, _1); BlockSignal(SIGTERM, fail_fn); @@ -1889,8 +1860,6 @@ static const JNINativeMethod gMethods[] = { (void *) com_android_internal_os_Zygote_nativeGetUsapPoolCount }, { "nativeEmptyUsapPool", "()V", (void *) com_android_internal_os_Zygote_nativeEmptyUsapPool }, - { "nativeDisableExecuteOnly", "()Z", - (void *) com_android_internal_os_Zygote_nativeDisableExecuteOnly }, { "nativeBlockSigTerm", "()V", (void* ) com_android_internal_os_Zygote_nativeBlockSigTerm }, { "nativeUnblockSigTerm", "()V", diff --git a/identity/java/android/security/identity/CredstoreIdentityCredential.java b/identity/java/android/security/identity/CredstoreIdentityCredential.java index c520331ab72d..7c0af6def696 100644 --- a/identity/java/android/security/identity/CredstoreIdentityCredential.java +++ b/identity/java/android/security/identity/CredstoreIdentityCredential.java @@ -152,8 +152,8 @@ class CredstoreIdentityCredential extends IdentityCredential { derivedKey = Util.computeHkdf("HmacSha256", sharedSecret, salt, info, 32); mReaderSecretKey = new SecretKeySpec(derivedKey, "AES"); - mEphemeralCounter = 0; - mReadersExpectedEphemeralCounter = 0; + mEphemeralCounter = 1; + mReadersExpectedEphemeralCounter = 1; } catch (NoSuchAlgorithmException e) { throw new RuntimeException("Error performing key agreement", e); diff --git a/packages/PrintSpooler/res/values-ja/donottranslate.xml b/packages/PrintSpooler/res/values-ja/donottranslate.xml index d334ddd312cf..6a0f768336ae 100644 --- a/packages/PrintSpooler/res/values-ja/donottranslate.xml +++ b/packages/PrintSpooler/res/values-ja/donottranslate.xml @@ -16,7 +16,7 @@ <resources> - <string name="mediasize_default">JIS_B5</string> + <string name="mediasize_default">ISO_A4</string> <string name="mediasize_standard">@string/mediasize_standard_japan</string> </resources> diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 57821c58f768..5e6ccc07c4da 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -109,7 +109,6 @@ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" /> - <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" /> <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /> <uses-permission android:name="android.permission.CREATE_USERS" /> diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java index f39e7af43ee6..2653b6d23ac9 100644 --- a/packages/Tethering/src/android/net/ip/IpServer.java +++ b/packages/Tethering/src/android/net/ip/IpServer.java @@ -756,7 +756,7 @@ public class IpServer extends StateMachine { final IpPrefix ipv4Prefix = new IpPrefix(mIpv4Address.getAddress(), mIpv4Address.getPrefixLength()); NetdUtils.tetherInterface(mNetd, mIfaceName, ipv4Prefix); - } catch (RemoteException | ServiceSpecificException e) { + } catch (RemoteException | ServiceSpecificException | IllegalStateException e) { mLog.e("Error Tethering: " + e); mLastError = TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR; return; diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index e2b8ad948d3c..1da208030d15 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -606,7 +606,7 @@ public class ConnectivityService extends IConnectivityManager.Stub private Set<String> mWolSupportedInterfaces; - private TelephonyManager mTelephonyManager; + private final TelephonyManager mTelephonyManager; private final AppOpsManager mAppOpsManager; private final LocationPermissionChecker mLocationPermissionChecker; @@ -957,6 +957,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mDeps = checkNotNull(deps, "missing Dependencies"); mSystemProperties = mDeps.getSystemProperties(); mNetIdManager = mDeps.makeNetIdManager(); + mContext = checkNotNull(context, "missing Context"); mMetricsLog = logger; mDefaultRequest = createDefaultInternetRequestForTransport(-1, NetworkRequest.Type.REQUEST); @@ -986,7 +987,6 @@ public class ConnectivityService extends IConnectivityManager.Stub mLingerDelayMs = mSystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS); - mContext = checkNotNull(context, "missing Context"); mNMS = checkNotNull(netManager, "missing INetworkManagementService"); mStatsService = checkNotNull(statsService, "missing INetworkStatsService"); mPolicyManager = checkNotNull(policyManager, "missing INetworkPolicyManager"); @@ -1166,6 +1166,7 @@ public class ConnectivityService extends IConnectivityManager.Stub int transportType, NetworkRequest.Type type) { final NetworkCapabilities netCap = new NetworkCapabilities(); netCap.addCapability(NET_CAPABILITY_INTERNET); + netCap.setRequestorUidAndPackageName(Process.myUid(), mContext.getPackageName()); if (transportType > -1) { netCap.addTransportType(transportType); } @@ -1696,10 +1697,12 @@ public class ConnectivityService extends IConnectivityManager.Stub return newLp; } - private void restrictRequestUidsForCaller(NetworkCapabilities nc) { + private void restrictRequestUidsForCallerAndSetRequestorInfo(NetworkCapabilities nc, + int callerUid, String callerPackageName) { if (!checkSettingsPermission()) { - nc.setSingleUid(Binder.getCallingUid()); + nc.setSingleUid(callerUid); } + nc.setRequestorUidAndPackageName(callerUid, callerPackageName); nc.setAdministratorUids(Collections.EMPTY_LIST); // Clear owner UID; this can never come from an app. @@ -5305,7 +5308,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // This checks that the passed capabilities either do not request a // specific SSID/SignalStrength, or the calling app has permission to do so. private void ensureSufficientPermissionsForRequest(NetworkCapabilities nc, - int callerPid, int callerUid) { + int callerPid, int callerUid, String callerPackageName) { if (null != nc.getSSID() && !checkSettingsPermission(callerPid, callerUid)) { throw new SecurityException("Insufficient permissions to request a specific SSID"); } @@ -5315,6 +5318,7 @@ public class ConnectivityService extends IConnectivityManager.Stub throw new SecurityException( "Insufficient permissions to request a specific signal strength"); } + mAppOpsManager.checkPackage(callerUid, callerPackageName); } private ArrayList<Integer> getSignalStrengthThresholds(NetworkAgentInfo nai) { @@ -5361,7 +5365,6 @@ public class ConnectivityService extends IConnectivityManager.Stub return; } MatchAllNetworkSpecifier.checkNotMatchAllNetworkSpecifier(ns); - ns.assertValidFromUid(Binder.getCallingUid()); } private void ensureValid(NetworkCapabilities nc) { @@ -5373,7 +5376,9 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities, - Messenger messenger, int timeoutMs, IBinder binder, int legacyType) { + Messenger messenger, int timeoutMs, IBinder binder, int legacyType, + @NonNull String callingPackageName) { + final int callingUid = Binder.getCallingUid(); final NetworkRequest.Type type = (networkCapabilities == null) ? NetworkRequest.Type.TRACK_DEFAULT : NetworkRequest.Type.REQUEST; @@ -5381,7 +5386,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // the default network request. This allows callers to keep track of // the system default network. if (type == NetworkRequest.Type.TRACK_DEFAULT) { - networkCapabilities = createDefaultNetworkCapabilitiesForUid(Binder.getCallingUid()); + networkCapabilities = createDefaultNetworkCapabilitiesForUid(callingUid); enforceAccessPermission(); } else { networkCapabilities = new NetworkCapabilities(networkCapabilities); @@ -5393,13 +5398,14 @@ public class ConnectivityService extends IConnectivityManager.Stub } ensureRequestableCapabilities(networkCapabilities); ensureSufficientPermissionsForRequest(networkCapabilities, - Binder.getCallingPid(), Binder.getCallingUid()); + Binder.getCallingPid(), callingUid, callingPackageName); // Set the UID range for this request to the single UID of the requester, or to an empty // set of UIDs if the caller has the appropriate permission and UIDs have not been set. // This will overwrite any allowed UIDs in the requested capabilities. Though there // are no visible methods to set the UIDs, an app could use reflection to try and get // networks for other apps so it's essential that the UIDs are overwritten. - restrictRequestUidsForCaller(networkCapabilities); + restrictRequestUidsForCallerAndSetRequestorInfo(networkCapabilities, + callingUid, callingPackageName); if (timeoutMs < 0) { throw new IllegalArgumentException("Bad timeout specified"); @@ -5474,16 +5480,18 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public NetworkRequest pendingRequestForNetwork(NetworkCapabilities networkCapabilities, - PendingIntent operation) { + PendingIntent operation, @NonNull String callingPackageName) { checkNotNull(operation, "PendingIntent cannot be null."); + final int callingUid = Binder.getCallingUid(); networkCapabilities = new NetworkCapabilities(networkCapabilities); enforceNetworkRequestPermissions(networkCapabilities); enforceMeteredApnPolicy(networkCapabilities); ensureRequestableCapabilities(networkCapabilities); ensureSufficientPermissionsForRequest(networkCapabilities, - Binder.getCallingPid(), Binder.getCallingUid()); + Binder.getCallingPid(), callingUid, callingPackageName); ensureValidNetworkSpecifier(networkCapabilities); - restrictRequestUidsForCaller(networkCapabilities); + restrictRequestUidsForCallerAndSetRequestorInfo(networkCapabilities, + callingUid, callingPackageName); NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, TYPE_NONE, nextNetworkRequestId(), NetworkRequest.Type.REQUEST); @@ -5531,15 +5539,16 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public NetworkRequest listenForNetwork(NetworkCapabilities networkCapabilities, - Messenger messenger, IBinder binder) { + Messenger messenger, IBinder binder, @NonNull String callingPackageName) { + final int callingUid = Binder.getCallingUid(); if (!hasWifiNetworkListenPermission(networkCapabilities)) { enforceAccessPermission(); } NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities); ensureSufficientPermissionsForRequest(networkCapabilities, - Binder.getCallingPid(), Binder.getCallingUid()); - restrictRequestUidsForCaller(nc); + Binder.getCallingPid(), callingUid, callingPackageName); + restrictRequestUidsForCallerAndSetRequestorInfo(nc, callingUid, callingPackageName); // Apps without the CHANGE_NETWORK_STATE permission can't use background networks, so // make all their listens include NET_CAPABILITY_FOREGROUND. That way, they will get // onLost and onAvailable callbacks when networks move in and out of the background. @@ -5559,17 +5568,17 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public void pendingListenForNetwork(NetworkCapabilities networkCapabilities, - PendingIntent operation) { + PendingIntent operation, @NonNull String callingPackageName) { checkNotNull(operation, "PendingIntent cannot be null."); + final int callingUid = Binder.getCallingUid(); if (!hasWifiNetworkListenPermission(networkCapabilities)) { enforceAccessPermission(); } ensureValid(networkCapabilities); ensureSufficientPermissionsForRequest(networkCapabilities, - Binder.getCallingPid(), Binder.getCallingUid()); - + Binder.getCallingPid(), callingUid, callingPackageName); final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities); - restrictRequestUidsForCaller(nc); + restrictRequestUidsForCallerAndSetRequestorInfo(nc, callingUid, callingPackageName); NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(), NetworkRequest.Type.LISTEN); @@ -6898,6 +6907,15 @@ public class ConnectivityService extends IConnectivityManager.Stub // worry about multiple different substates of CONNECTED. newInfo.setDetailedState(NetworkInfo.DetailedState.SUSPENDED, info.getReason(), info.getExtraInfo()); + } else if (!suspended && info.getDetailedState() == NetworkInfo.DetailedState.SUSPENDED) { + // SUSPENDED state is currently only overridden from CONNECTED state. In the case the + // network agent is created, then goes to suspended, then goes out of suspended without + // ever setting connected. Check if network agent is ever connected to update the state. + newInfo.setDetailedState(nai.everConnected + ? NetworkInfo.DetailedState.CONNECTED + : NetworkInfo.DetailedState.CONNECTING, + info.getReason(), + info.getExtraInfo()); } newInfo.setRoaming(!nai.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_ROAMING)); return newInfo; @@ -7848,12 +7866,13 @@ public class ConnectivityService extends IConnectivityManager.Stub throw new IllegalArgumentException("ConnectivityManager.TYPE_* are deprecated." + " Please use NetworkCapabilities instead."); } - mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackageName); + final int callingUid = Binder.getCallingUid(); + mAppOpsManager.checkPackage(callingUid, callingPackageName); // This NetworkCapabilities is only used for matching to Networks. Clear out its owner uid // and administrator uids to be safe. final NetworkCapabilities nc = new NetworkCapabilities(request.networkCapabilities); - restrictRequestUidsForCaller(nc); + restrictRequestUidsForCallerAndSetRequestorInfo(nc, callingUid, callingPackageName); final NetworkRequest requestWithId = new NetworkRequest( diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java index a629b3fbb8fa..98ac4cb7122a 100644 --- a/services/core/java/com/android/server/IpSecService.java +++ b/services/core/java/com/android/server/IpSecService.java @@ -1557,16 +1557,16 @@ public class IpSecService extends IIpSecService.Stub { } checkNotNull(callingPackage, "Null calling package cannot create IpSec tunnels"); - switch (getAppOpsManager().noteOp(TUNNEL_OP, Binder.getCallingUid(), callingPackage)) { - case AppOpsManager.MODE_DEFAULT: - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService"); - break; - case AppOpsManager.MODE_ALLOWED: - return; - default: - throw new SecurityException("Request to ignore AppOps for non-legacy API"); + + // OP_MANAGE_IPSEC_TUNNELS will return MODE_ERRORED by default, including for the system + // server. If the appop is not granted, require that the caller has the MANAGE_IPSEC_TUNNELS + // permission or is the System Server. + if (AppOpsManager.MODE_ALLOWED == getAppOpsManager().noteOpNoThrow( + TUNNEL_OP, Binder.getCallingUid(), callingPackage)) { + return; } + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService"); } private void createOrUpdateTransform( diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 88a63f1aa06d..0cfa9fd4b39b 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -124,6 +124,7 @@ import android.util.Log; import android.util.MathUtils; import android.util.PrintWriterPrinter; import android.util.Slog; +import android.util.SparseArray; import android.util.SparseIntArray; import android.view.KeyEvent; import android.view.accessibility.AccessibilityManager; @@ -243,6 +244,7 @@ public class AudioService extends IAudioService.Stub // AudioHandler messages private static final int MSG_SET_DEVICE_VOLUME = 0; private static final int MSG_PERSIST_VOLUME = 1; + private static final int MSG_PERSIST_VOLUME_GROUP = 2; private static final int MSG_PERSIST_RINGER_MODE = 3; private static final int MSG_AUDIO_SERVER_DIED = 4; private static final int MSG_PLAY_SOUND_EFFECT = 5; @@ -761,6 +763,10 @@ public class AudioService extends IAudioService.Stub mSettingsObserver = new SettingsObserver(); createStreamStates(); + // must be called after createStreamStates() as it uses MUSIC volume as default if no + // persistent data + initVolumeGroupStates(); + // mSafeUsbMediaVolumeIndex must be initialized after createStreamStates() because it // relies on audio policy having correct ranges for volume indexes. mSafeUsbMediaVolumeIndex = getSafeUsbMediaVolumeIndex(); @@ -995,6 +1001,9 @@ public class AudioService extends IAudioService.Stub streamState.applyAllVolumes(); } + // Restore audio volume groups + restoreVolumeGroups(); + // Restore mono mode updateMasterMono(mContentResolver); @@ -2112,20 +2121,20 @@ public class AudioService extends IAudioService.Stub String callingPackage) { enforceModifyAudioRoutingPermission(); Preconditions.checkNotNull(attr, "attr must not be null"); - // @todo not hold the caller context, post message - int stream = AudioProductStrategy.getLegacyStreamTypeForStrategyWithAudioAttributes(attr); - final int device = getDeviceForStream(stream); - - int oldIndex = AudioSystem.getVolumeIndexForAttributes(attr, device); - - AudioSystem.setVolumeIndexForAttributes(attr, index, device); - final int volumeGroup = getVolumeGroupIdForAttributes(attr); - final AudioVolumeGroup avg = getAudioVolumeGroupById(volumeGroup); - if (avg == null) { + if (sVolumeGroupStates.indexOfKey(volumeGroup) < 0) { + Log.e(TAG, ": no volume group found for attributes " + attr.toString()); return; } - for (final int groupedStream : avg.getLegacyStreamTypes()) { + final VolumeGroupState vgs = sVolumeGroupStates.get(volumeGroup); + + sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_SET_GROUP_VOL, attr, vgs.name(), + index/*val1*/, flags/*val2*/, callingPackage)); + + vgs.setVolumeIndex(index, flags); + + // For legacy reason, propagate to all streams associated to this volume group + for (final int groupedStream : vgs.getLegacyStreamTypes()) { setStreamVolume(groupedStream, index, flags, callingPackage, callingPackage, Binder.getCallingUid()); } @@ -2147,10 +2156,12 @@ public class AudioService extends IAudioService.Stub public int getVolumeIndexForAttributes(@NonNull AudioAttributes attr) { enforceModifyAudioRoutingPermission(); Preconditions.checkNotNull(attr, "attr must not be null"); - int stream = AudioProductStrategy.getLegacyStreamTypeForStrategyWithAudioAttributes(attr); - final int device = getDeviceForStream(stream); - - return AudioSystem.getVolumeIndexForAttributes(attr, device); + final int volumeGroup = getVolumeGroupIdForAttributes(attr); + if (sVolumeGroupStates.indexOfKey(volumeGroup) < 0) { + throw new IllegalArgumentException("No volume group for attributes " + attr); + } + final VolumeGroupState vgs = sVolumeGroupStates.get(volumeGroup); + return vgs.getVolumeIndex(); } /** @see AudioManager#getMaxVolumeIndexForAttributes(attr) */ @@ -3549,6 +3560,8 @@ public class AudioService extends IAudioService.Stub enforceSafeMediaVolume(TAG); } } + + readVolumeGroupsSettings(); } /** @see AudioManager#setSpeakerphoneOn(boolean) */ @@ -4437,6 +4450,310 @@ public class AudioService extends IAudioService.Stub /////////////////////////////////////////////////////////////////////////// // Inner classes /////////////////////////////////////////////////////////////////////////// + /** + * Key is the AudioManager VolumeGroupId + * Value is the VolumeGroupState + */ + private static final SparseArray<VolumeGroupState> sVolumeGroupStates = new SparseArray<>(); + + private void initVolumeGroupStates() { + for (final AudioVolumeGroup avg : getAudioVolumeGroups()) { + try { + // if no valid attributes, this volume group is not controllable, throw exception + ensureValidAttributes(avg); + } catch (IllegalArgumentException e) { + // Volume Groups without attributes are not controllable through set/get volume + // using attributes. Do not append them. + Log.d(TAG, "volume group " + avg.name() + " for internal policy needs"); + continue; + } + sVolumeGroupStates.append(avg.getId(), new VolumeGroupState(avg)); + } + for (int i = 0; i < sVolumeGroupStates.size(); i++) { + final VolumeGroupState vgs = sVolumeGroupStates.valueAt(i); + vgs.applyAllVolumes(); + } + } + + private void ensureValidAttributes(AudioVolumeGroup avg) { + boolean hasAtLeastOneValidAudioAttributes = avg.getAudioAttributes().stream() + .anyMatch(aa -> !aa.equals(AudioProductStrategy.sDefaultAttributes)); + if (!hasAtLeastOneValidAudioAttributes) { + throw new IllegalArgumentException("Volume Group " + avg.name() + + " has no valid audio attributes"); + } + } + + private void readVolumeGroupsSettings() { + Log.v(TAG, "readVolumeGroupsSettings"); + for (int i = 0; i < sVolumeGroupStates.size(); i++) { + final VolumeGroupState vgs = sVolumeGroupStates.valueAt(i); + vgs.readSettings(); + vgs.applyAllVolumes(); + } + } + + // Called upon crash of AudioServer + private void restoreVolumeGroups() { + Log.v(TAG, "restoreVolumeGroups"); + for (int i = 0; i < sVolumeGroupStates.size(); i++) { + final VolumeGroupState vgs = sVolumeGroupStates.valueAt(i); + vgs.applyAllVolumes(); + } + } + + private void dumpVolumeGroups(PrintWriter pw) { + pw.println("\nVolume Groups (device: index)"); + for (int i = 0; i < sVolumeGroupStates.size(); i++) { + final VolumeGroupState vgs = sVolumeGroupStates.valueAt(i); + vgs.dump(pw); + pw.println(""); + } + } + + // NOTE: Locking order for synchronized objects related to volume management: + // 1 mSettingsLock + // 2 VolumeGroupState.class + private class VolumeGroupState { + private final AudioVolumeGroup mAudioVolumeGroup; + private final SparseIntArray mIndexMap = new SparseIntArray(8); + private int mIndexMin; + private int mIndexMax; + private int mLegacyStreamType = AudioSystem.STREAM_DEFAULT; + private int mPublicStreamType = AudioSystem.STREAM_MUSIC; + private AudioAttributes mAudioAttributes = AudioProductStrategy.sDefaultAttributes; + + // No API in AudioSystem to get a device from strategy or from attributes. + // Need a valid public stream type to use current API getDeviceForStream + private int getDeviceForVolume() { + return getDeviceForStream(mPublicStreamType); + } + + private VolumeGroupState(AudioVolumeGroup avg) { + mAudioVolumeGroup = avg; + Log.v(TAG, "VolumeGroupState for " + avg.toString()); + for (final AudioAttributes aa : avg.getAudioAttributes()) { + if (!aa.equals(AudioProductStrategy.sDefaultAttributes)) { + mAudioAttributes = aa; + break; + } + } + final int[] streamTypes = mAudioVolumeGroup.getLegacyStreamTypes(); + if (streamTypes.length != 0) { + // Uses already initialized MIN / MAX if a stream type is attached to group + mLegacyStreamType = streamTypes[0]; + for (final int streamType : streamTypes) { + if (streamType != AudioSystem.STREAM_DEFAULT + && streamType < AudioSystem.getNumStreamTypes()) { + mPublicStreamType = streamType; + break; + } + } + mIndexMin = MIN_STREAM_VOLUME[mPublicStreamType]; + mIndexMax = MAX_STREAM_VOLUME[mPublicStreamType]; + } else if (!avg.getAudioAttributes().isEmpty()) { + mIndexMin = AudioSystem.getMinVolumeIndexForAttributes(mAudioAttributes); + mIndexMax = AudioSystem.getMaxVolumeIndexForAttributes(mAudioAttributes); + } else { + Log.e(TAG, "volume group: " + mAudioVolumeGroup.name() + + " has neither valid attributes nor valid stream types assigned"); + return; + } + // Load volume indexes from data base + readSettings(); + } + + public @NonNull int[] getLegacyStreamTypes() { + return mAudioVolumeGroup.getLegacyStreamTypes(); + } + + public String name() { + return mAudioVolumeGroup.name(); + } + + public int getVolumeIndex() { + return getIndex(getDeviceForVolume()); + } + + public void setVolumeIndex(int index, int flags) { + if (mUseFixedVolume) { + return; + } + setVolumeIndex(index, getDeviceForVolume(), flags); + } + + private void setVolumeIndex(int index, int device, int flags) { + // Set the volume index + setVolumeIndexInt(index, device, flags); + + // Update local cache + mIndexMap.put(device, index); + + // update data base - post a persist volume group msg + sendMsg(mAudioHandler, + MSG_PERSIST_VOLUME_GROUP, + SENDMSG_QUEUE, + device, + 0, + this, + PERSIST_DELAY); + } + + private void setVolumeIndexInt(int index, int device, int flags) { + // Set the volume index + AudioSystem.setVolumeIndexForAttributes(mAudioAttributes, index, device); + } + + public int getIndex(int device) { + synchronized (VolumeGroupState.class) { + int index = mIndexMap.get(device, -1); + // there is always an entry for AudioSystem.DEVICE_OUT_DEFAULT + return (index != -1) ? index : mIndexMap.get(AudioSystem.DEVICE_OUT_DEFAULT); + } + } + + public boolean hasIndexForDevice(int device) { + synchronized (VolumeGroupState.class) { + return (mIndexMap.get(device, -1) != -1); + } + } + + public int getMaxIndex() { + return mIndexMax; + } + + public int getMinIndex() { + return mIndexMin; + } + + public void applyAllVolumes() { + synchronized (VolumeGroupState.class) { + if (mLegacyStreamType != AudioSystem.STREAM_DEFAULT) { + // No-op to avoid regression with stream based volume management + return; + } + // apply device specific volumes first + int index; + for (int i = 0; i < mIndexMap.size(); i++) { + final int device = mIndexMap.keyAt(i); + if (device != AudioSystem.DEVICE_OUT_DEFAULT) { + index = mIndexMap.valueAt(i); + Log.v(TAG, "applyAllVolumes: restore index " + index + " for group " + + mAudioVolumeGroup.name() + " and device " + + AudioSystem.getOutputDeviceName(device)); + setVolumeIndexInt(index, device, 0 /*flags*/); + } + } + // apply default volume last: by convention , default device volume will be used + index = getIndex(AudioSystem.DEVICE_OUT_DEFAULT); + Log.v(TAG, "applyAllVolumes: restore default device index " + index + " for group " + + mAudioVolumeGroup.name()); + setVolumeIndexInt(index, AudioSystem.DEVICE_OUT_DEFAULT, 0 /*flags*/); + } + } + + private void persistVolumeGroup(int device) { + if (mUseFixedVolume) { + return; + } + Log.v(TAG, "persistVolumeGroup: storing index " + getIndex(device) + " for group " + + mAudioVolumeGroup.name() + " and device " + + AudioSystem.getOutputDeviceName(device)); + boolean success = Settings.System.putIntForUser(mContentResolver, + getSettingNameForDevice(device), + getIndex(device), + UserHandle.USER_CURRENT); + if (!success) { + Log.e(TAG, "persistVolumeGroup failed for group " + mAudioVolumeGroup.name()); + } + } + + public void readSettings() { + synchronized (VolumeGroupState.class) { + // First clear previously loaded (previous user?) settings + mIndexMap.clear(); + // force maximum volume on all streams if fixed volume property is set + if (mUseFixedVolume) { + mIndexMap.put(AudioSystem.DEVICE_OUT_DEFAULT, mIndexMax); + return; + } + for (int device : AudioSystem.DEVICE_OUT_ALL_SET) { + // retrieve current volume for device + // if no volume stored for current volume group and device, use default volume + // if default device, continue otherwise + int defaultIndex = (device == AudioSystem.DEVICE_OUT_DEFAULT) + ? AudioSystem.DEFAULT_STREAM_VOLUME[mPublicStreamType] : -1; + int index; + String name = getSettingNameForDevice(device); + index = Settings.System.getIntForUser( + mContentResolver, name, defaultIndex, UserHandle.USER_CURRENT); + if (index == -1) { + Log.e(TAG, "readSettings: No index stored for group " + + mAudioVolumeGroup.name() + ", device " + name); + continue; + } + Log.v(TAG, "readSettings: found stored index " + getValidIndex(index) + + " for group " + mAudioVolumeGroup.name() + ", device: " + name); + mIndexMap.put(device, getValidIndex(index)); + } + } + } + + private int getValidIndex(int index) { + if (index < mIndexMin) { + return mIndexMin; + } else if (mUseFixedVolume || index > mIndexMax) { + return mIndexMax; + } + return index; + } + + public @NonNull String getSettingNameForDevice(int device) { + final String suffix = AudioSystem.getOutputDeviceName(device); + if (suffix.isEmpty()) { + return mAudioVolumeGroup.name(); + } + return mAudioVolumeGroup.name() + "_" + AudioSystem.getOutputDeviceName(device); + } + + private void dump(PrintWriter pw) { + pw.println("- VOLUME GROUP " + mAudioVolumeGroup.name() + ":"); + pw.print(" Min: "); + pw.println(mIndexMin); + pw.print(" Max: "); + pw.println(mIndexMax); + pw.print(" Current: "); + for (int i = 0; i < mIndexMap.size(); i++) { + if (i > 0) { + pw.print(", "); + } + final int device = mIndexMap.keyAt(i); + pw.print(Integer.toHexString(device)); + final String deviceName = device == AudioSystem.DEVICE_OUT_DEFAULT ? "default" + : AudioSystem.getOutputDeviceName(device); + if (!deviceName.isEmpty()) { + pw.print(" ("); + pw.print(deviceName); + pw.print(")"); + } + pw.print(": "); + pw.print(mIndexMap.valueAt(i)); + } + pw.println(); + pw.print(" Devices: "); + int n = 0; + final int devices = getDeviceForVolume(); + for (int device : AudioSystem.DEVICE_OUT_ALL_SET) { + if ((devices & device) == device) { + if (n++ > 0) { + pw.print(", "); + } + pw.print(AudioSystem.getOutputDeviceName(device)); + } + } + } + } + // NOTE: Locking order for synchronized objects related to volume or ringer mode management: // 1 mScoclient OR mSafeMediaVolumeState @@ -5081,6 +5398,11 @@ public class AudioService extends IAudioService.Stub persistVolume((VolumeStreamState) msg.obj, msg.arg1); break; + case MSG_PERSIST_VOLUME_GROUP: + final VolumeGroupState vgs = (VolumeGroupState) msg.obj; + vgs.persistVolumeGroup(msg.arg1); + break; + case MSG_PERSIST_RINGER_MODE: // note that the value persisted is the current ringer mode, not the // value of ringer mode as of the time the request was made to persist @@ -6128,6 +6450,7 @@ public class AudioService extends IAudioService.Stub } mMediaFocusControl.dump(pw); dumpStreamStates(pw); + dumpVolumeGroups(pw); dumpRingerMode(pw); pw.println("\nAudio routes:"); pw.print(" mMainType=0x"); pw.println(Integer.toHexString( diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java index fcd8701f4ccf..add620e74df0 100644 --- a/services/core/java/com/android/server/audio/AudioServiceEvents.java +++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java @@ -16,6 +16,7 @@ package com.android.server.audio; +import android.media.AudioAttributes; import android.media.AudioManager; import android.media.AudioSystem; @@ -97,12 +98,15 @@ public class AudioServiceEvents { static final int VOL_ADJUST_VOL_UID = 5; static final int VOL_VOICE_ACTIVITY_HEARING_AID = 6; static final int VOL_MODE_CHANGE_HEARING_AID = 7; + static final int VOL_SET_GROUP_VOL = 8; final int mOp; final int mStream; final int mVal1; final int mVal2; final String mCaller; + final String mGroupName; + final AudioAttributes mAudioAttributes; /** used for VOL_ADJUST_VOL_UID, * VOL_ADJUST_SUGG_VOL, @@ -114,6 +118,8 @@ public class AudioServiceEvents { mVal1 = val1; mVal2 = val2; mCaller = caller; + mGroupName = null; + mAudioAttributes = null; } /** used for VOL_SET_HEARING_AID_VOL*/ @@ -124,6 +130,8 @@ public class AudioServiceEvents { // unused mStream = -1; mCaller = null; + mGroupName = null; + mAudioAttributes = null; } /** used for VOL_SET_AVRCP_VOL */ @@ -134,6 +142,8 @@ public class AudioServiceEvents { mVal2 = 0; mStream = -1; mCaller = null; + mGroupName = null; + mAudioAttributes = null; } /** used for VOL_VOICE_ACTIVITY_HEARING_AID */ @@ -144,6 +154,8 @@ public class AudioServiceEvents { mVal2 = voiceActive ? 1 : 0; // unused mCaller = null; + mGroupName = null; + mAudioAttributes = null; } /** used for VOL_MODE_CHANGE_HEARING_AID */ @@ -154,6 +166,19 @@ public class AudioServiceEvents { mVal2 = mode; // unused mCaller = null; + mGroupName = null; + mAudioAttributes = null; + } + + /** used for VOL_SET_GROUP_VOL */ + VolumeEvent(int op, AudioAttributes aa, String group, int index, int flags, String caller) { + mOp = op; + mStream = -1; + mVal1 = index; + mVal2 = flags; + mCaller = caller; + mGroupName = group; + mAudioAttributes = aa; } @Override @@ -208,6 +233,14 @@ public class AudioServiceEvents { .append(") causes setting HEARING_AID volume to idx:").append(mVal1) .append(" stream:").append(AudioSystem.streamToString(mStream)) .toString(); + case VOL_SET_GROUP_VOL: + return new StringBuilder("setVolumeIndexForAttributes(attr:") + .append(mAudioAttributes.toString()) + .append(" group: ").append(mGroupName) + .append(" index:").append(mVal1) + .append(" flags:0x").append(Integer.toHexString(mVal2)) + .append(") from ").append(mCaller) + .toString(); default: return new StringBuilder("FIXME invalid op:").append(mOp).toString(); } } diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java index 2eec4199217f..8687f35d745e 100644 --- a/services/core/java/com/android/server/compat/CompatChange.java +++ b/services/core/java/com/android/server/compat/CompatChange.java @@ -17,6 +17,7 @@ package com.android.server.compat; import android.annotation.Nullable; +import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; import android.content.pm.ApplicationInfo; @@ -39,6 +40,13 @@ import java.util.Map; public final class CompatChange extends CompatibilityChangeInfo { /** + * A change ID to be used only in the CTS test for this SystemApi + */ + @ChangeId + @EnabledAfter(targetSdkVersion = 1234) // Needs to be > test APK targetSdkVersion. + private static final long CTS_SYSTEM_API_CHANGEID = 149391281; // This is a bug id. + + /** * Callback listener for when compat changes are updated for a package. * See {@link #registerListener(ChangeListener)} for more details. */ diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 99560226f99a..7f6dc55a369d 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -48,8 +48,12 @@ import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; import android.net.ConnectivityManager; import android.net.INetworkManagementEventObserver; +import android.net.Ikev2VpnProfile; import android.net.IpPrefix; import android.net.IpSecManager; +import android.net.IpSecManager.IpSecTunnelInterface; +import android.net.IpSecManager.UdpEncapsulationSocket; +import android.net.IpSecTransform; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.LocalSocket; @@ -65,6 +69,12 @@ import android.net.RouteInfo; import android.net.UidRange; import android.net.VpnManager; import android.net.VpnService; +import android.net.ipsec.ike.ChildSessionCallback; +import android.net.ipsec.ike.ChildSessionConfiguration; +import android.net.ipsec.ike.ChildSessionParams; +import android.net.ipsec.ike.IkeSession; +import android.net.ipsec.ike.IkeSessionCallback; +import android.net.ipsec.ike.IkeSessionParams; import android.os.Binder; import android.os.Build.VERSION_CODES; import android.os.Bundle; @@ -113,6 +123,7 @@ import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -122,6 +133,9 @@ import java.util.Objects; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; /** @@ -176,14 +190,14 @@ public class Vpn { private final Context mContext; private final NetworkInfo mNetworkInfo; - private String mPackage; + @VisibleForTesting protected String mPackage; private int mOwnerUID; private boolean mIsPackageTargetingAtLeastQ; private String mInterface; private Connection mConnection; /** Tracks the runners for all VPN types managed by the platform (eg. LegacyVpn, PlatformVpn) */ - private VpnRunner mVpnRunner; + @VisibleForTesting protected VpnRunner mVpnRunner; private PendingIntent mStatusIntent; private volatile boolean mEnableTeardown = true; @@ -196,6 +210,7 @@ public class Vpn { @VisibleForTesting protected final NetworkCapabilities mNetworkCapabilities; private final SystemServices mSystemServices; + private final Ikev2SessionCreator mIkev2SessionCreator; /** * Whether to keep the connection active after rebooting, or upgrading or reinstalling. This @@ -238,17 +253,20 @@ public class Vpn { public Vpn(Looper looper, Context context, INetworkManagementService netService, @UserIdInt int userHandle) { - this(looper, context, netService, userHandle, new SystemServices(context)); + this(looper, context, netService, userHandle, + new SystemServices(context), new Ikev2SessionCreator()); } @VisibleForTesting protected Vpn(Looper looper, Context context, INetworkManagementService netService, - int userHandle, SystemServices systemServices) { + int userHandle, SystemServices systemServices, + Ikev2SessionCreator ikev2SessionCreator) { mContext = context; mNetd = netService; mUserHandle = userHandle; mLooper = looper; mSystemServices = systemServices; + mIkev2SessionCreator = ikev2SessionCreator; mPackage = VpnConfig.LEGACY_VPN; mOwnerUID = getAppUid(mPackage, mUserHandle); @@ -749,8 +767,9 @@ public class Vpn { private boolean isCurrentPreparedPackage(String packageName) { // We can't just check that packageName matches mPackage, because if the app was uninstalled - // and reinstalled it will no longer be prepared. Instead check the UID. - return getAppUid(packageName, mUserHandle) == mOwnerUID; + // and reinstalled it will no longer be prepared. Similarly if there is a shared UID, the + // calling package may not be the same as the prepared package. Check both UID and package. + return getAppUid(packageName, mUserHandle) == mOwnerUID && mPackage.equals(packageName); } /** Prepare the VPN for the given package. Does not perform permission checks. */ @@ -975,7 +994,11 @@ public class Vpn { } lp.setDomains(buffer.toString().trim()); - // TODO: Stop setting the MTU in jniCreate and set it here. + if (mConfig.mtu > 0) { + lp.setMtu(mConfig.mtu); + } + + // TODO: Stop setting the MTU in jniCreate return lp; } @@ -2000,30 +2023,369 @@ public class Vpn { protected abstract void exit(); } - private class IkeV2VpnRunner extends VpnRunner { - private static final String TAG = "IkeV2VpnRunner"; + interface IkeV2VpnRunnerCallback { + void onDefaultNetworkChanged(@NonNull Network network); - private final IpSecManager mIpSecManager; - private final VpnProfile mProfile; + void onChildOpened( + @NonNull Network network, @NonNull ChildSessionConfiguration childConfig); + + void onChildTransformCreated( + @NonNull Network network, @NonNull IpSecTransform transform, int direction); + + void onSessionLost(@NonNull Network network); + } + + /** + * Internal class managing IKEv2/IPsec VPN connectivity + * + * <p>The IKEv2 VPN will listen to, and run based on the lifecycle of Android's default Network. + * As a new default is selected, old IKE sessions will be torn down, and a new one will be + * started. + * + * <p>This class uses locking minimally - the Vpn instance lock is only ever held when fields of + * the outer class are modified. As such, care must be taken to ensure that no calls are added + * that might modify the outer class' state without acquiring a lock. + * + * <p>The overall structure of the Ikev2VpnRunner is as follows: + * + * <ol> + * <li>Upon startup, a NetworkRequest is registered with ConnectivityManager. This is called + * any time a new default network is selected + * <li>When a new default is connected, an IKE session is started on that Network. If there + * were any existing IKE sessions on other Networks, they are torn down before starting + * the new IKE session + * <li>Upon establishment, the onChildTransformCreated() callback is called twice, one for + * each direction, and finally onChildOpened() is called + * <li>Upon the onChildOpened() call, the VPN is fully set up. + * <li>Subsequent Network changes result in new onDefaultNetworkChanged() callbacks. See (2). + * </ol> + */ + class IkeV2VpnRunner extends VpnRunner implements IkeV2VpnRunnerCallback { + @NonNull private static final String TAG = "IkeV2VpnRunner"; + + @NonNull private final IpSecManager mIpSecManager; + @NonNull private final Ikev2VpnProfile mProfile; + @NonNull private final ConnectivityManager.NetworkCallback mNetworkCallback; + + /** + * Executor upon which ALL callbacks must be run. + * + * <p>This executor MUST be a single threaded executor, in order to ensure the consistency + * of the mutable Ikev2VpnRunner fields. The Ikev2VpnRunner is built mostly lock-free by + * virtue of everything being serialized on this executor. + */ + @NonNull private final ExecutorService mExecutor = Executors.newSingleThreadExecutor(); - IkeV2VpnRunner(VpnProfile profile) { + /** Signal to ensure shutdown is honored even if a new Network is connected. */ + private boolean mIsRunning = true; + + @Nullable private UdpEncapsulationSocket mEncapSocket; + @Nullable private IpSecTunnelInterface mTunnelIface; + @Nullable private IkeSession mSession; + @Nullable private Network mActiveNetwork; + + IkeV2VpnRunner(@NonNull Ikev2VpnProfile profile) { super(TAG); mProfile = profile; - - // TODO: move this to startVpnRunnerPrivileged() - mConfig = new VpnConfig(); - mIpSecManager = mContext.getSystemService(IpSecManager.class); + mIpSecManager = (IpSecManager) mContext.getSystemService(Context.IPSEC_SERVICE); + mNetworkCallback = new VpnIkev2Utils.Ikev2VpnNetworkCallback(TAG, this); } @Override public void run() { - // TODO: Build IKE config, start IKE session + // Explicitly use only the network that ConnectivityService thinks is the "best." In + // other words, only ever use the currently selected default network. This does mean + // that in both onLost() and onConnected(), any old sessions MUST be torn down. This + // does NOT include VPNs. + final ConnectivityManager cm = ConnectivityManager.from(mContext); + cm.requestNetwork(cm.getDefaultRequest(), mNetworkCallback); + } + + private boolean isActiveNetwork(@Nullable Network network) { + return Objects.equals(mActiveNetwork, network) && mIsRunning; + } + + /** + * Called when an IKE Child session has been opened, signalling completion of the startup. + * + * <p>This method is only ever called once per IkeSession, and MUST run on the mExecutor + * thread in order to ensure consistency of the Ikev2VpnRunner fields. + */ + public void onChildOpened( + @NonNull Network network, @NonNull ChildSessionConfiguration childConfig) { + if (!isActiveNetwork(network)) { + Log.d(TAG, "onOpened called for obsolete network " + network); + + // Do nothing; this signals that either: (1) a new/better Network was found, + // and the Ikev2VpnRunner has switched to it in onDefaultNetworkChanged, or (2) this + // IKE session was already shut down (exited, or an error was encountered somewhere + // else). In both cases, all resources and sessions are torn down via + // resetIkeState(). + return; + } + + try { + final String interfaceName = mTunnelIface.getInterfaceName(); + final int maxMtu = mProfile.getMaxMtu(); + final List<LinkAddress> internalAddresses = childConfig.getInternalAddresses(); + + final Collection<RouteInfo> newRoutes = VpnIkev2Utils.getRoutesFromTrafficSelectors( + childConfig.getOutboundTrafficSelectors()); + for (final LinkAddress address : internalAddresses) { + mTunnelIface.addAddress(address.getAddress(), address.getPrefixLength()); + } + + final NetworkAgent networkAgent; + final LinkProperties lp; + + synchronized (Vpn.this) { + mInterface = interfaceName; + mConfig.mtu = maxMtu; + mConfig.interfaze = mInterface; + + mConfig.addresses.clear(); + mConfig.addresses.addAll(internalAddresses); + + mConfig.routes.clear(); + mConfig.routes.addAll(newRoutes); + + // TODO: Add DNS servers from negotiation + + networkAgent = mNetworkAgent; + + // The below must be done atomically with the mConfig update, otherwise + // isRunningLocked() will be racy. + if (networkAgent == null) { + if (isSettingsVpnLocked()) { + prepareStatusIntent(); + } + agentConnect(); + return; // Link properties are already sent. + } + + lp = makeLinkProperties(); // Accesses VPN instance fields; must be locked + } + + networkAgent.sendLinkProperties(lp); + } catch (Exception e) { + Log.d(TAG, "Error in ChildOpened for network " + network, e); + onSessionLost(network); + } + } + + /** + * Called when an IPsec transform has been created, and should be applied. + * + * <p>This method is called multiple times over the lifetime of an IkeSession (or default + * network), and is MUST always be called on the mExecutor thread in order to ensure + * consistency of the Ikev2VpnRunner fields. + */ + public void onChildTransformCreated( + @NonNull Network network, @NonNull IpSecTransform transform, int direction) { + if (!isActiveNetwork(network)) { + Log.d(TAG, "ChildTransformCreated for obsolete network " + network); + + // Do nothing; this signals that either: (1) a new/better Network was found, + // and the Ikev2VpnRunner has switched to it in onDefaultNetworkChanged, or (2) this + // IKE session was already shut down (exited, or an error was encountered somewhere + // else). In both cases, all resources and sessions are torn down via + // resetIkeState(). + return; + } + + try { + // Transforms do not need to be persisted; the IkeSession will keep + // them alive for us + mIpSecManager.applyTunnelModeTransform(mTunnelIface, direction, transform); + } catch (IOException e) { + Log.d(TAG, "Transform application failed for network " + network, e); + onSessionLost(network); + } + } + + /** + * Called when a new default network is connected. + * + * <p>The Ikev2VpnRunner will unconditionally switch to the new network, killing the old IKE + * state in the process, and starting a new IkeSession instance. + * + * <p>This method is called multiple times over the lifetime of the Ikev2VpnRunner, and is + * called on the ConnectivityService thread. Thus, the actual work MUST be proxied to the + * mExecutor thread in order to ensure consistency of the Ikev2VpnRunner fields. + */ + public void onDefaultNetworkChanged(@NonNull Network network) { + Log.d(TAG, "Starting IKEv2/IPsec session on new network: " + network); + + // Proxy to the Ikev2VpnRunner (single-thread) executor to ensure consistency in lieu + // of locking. + mExecutor.execute(() -> { + try { + if (!mIsRunning) { + Log.d(TAG, "onDefaultNetworkChanged after exit"); + return; // VPN has been shut down. + } + + // Without MOBIKE, we have no way to seamlessly migrate. Close on old + // (non-default) network, and start the new one. + resetIkeState(); + mActiveNetwork = network; + + // TODO(b/149356682): Update this based on new IKE API + mEncapSocket = mIpSecManager.openUdpEncapsulationSocket(); + + // TODO(b/149356682): Update this based on new IKE API + final IkeSessionParams ikeSessionParams = + VpnIkev2Utils.buildIkeSessionParams(mProfile, mEncapSocket); + final ChildSessionParams childSessionParams = + VpnIkev2Utils.buildChildSessionParams(); + + // TODO: Remove the need for adding two unused addresses with + // IPsec tunnels. + mTunnelIface = + mIpSecManager.createIpSecTunnelInterface( + ikeSessionParams.getServerAddress() /* unused */, + ikeSessionParams.getServerAddress() /* unused */, + network); + mNetd.setInterfaceUp(mTunnelIface.getInterfaceName()); + + // Socket must be bound to prevent network switches from causing + // the IKE teardown to fail/timeout. + // TODO(b/149356682): Update this based on new IKE API + network.bindSocket(mEncapSocket.getFileDescriptor()); + + mSession = mIkev2SessionCreator.createIkeSession( + mContext, + ikeSessionParams, + childSessionParams, + mExecutor, + new VpnIkev2Utils.IkeSessionCallbackImpl( + TAG, IkeV2VpnRunner.this, network), + new VpnIkev2Utils.ChildSessionCallbackImpl( + TAG, IkeV2VpnRunner.this, network)); + Log.d(TAG, "Ike Session started for network " + network); + } catch (Exception e) { + Log.i(TAG, "Setup failed for network " + network + ". Aborting", e); + onSessionLost(network); + } + }); + } + + /** + * Handles loss of a session + * + * <p>The loss of a session might be due to an onLost() call, the IKE session getting torn + * down for any reason, or an error in updating state (transform application, VPN setup) + * + * <p>This method MUST always be called on the mExecutor thread in order to ensure + * consistency of the Ikev2VpnRunner fields. + */ + public void onSessionLost(@NonNull Network network) { + if (!isActiveNetwork(network)) { + Log.d(TAG, "onSessionLost() called for obsolete network " + network); + + // Do nothing; this signals that either: (1) a new/better Network was found, + // and the Ikev2VpnRunner has switched to it in onDefaultNetworkChanged, or (2) this + // IKE session was already shut down (exited, or an error was encountered somewhere + // else). In both cases, all resources and sessions are torn down via + // onSessionLost() and resetIkeState(). + return; + } + + mActiveNetwork = null; + + // Close all obsolete state, but keep VPN alive incase a usable network comes up. + // (Mirrors VpnService behavior) + Log.d(TAG, "Resetting state for network: " + network); + + synchronized (Vpn.this) { + // Since this method handles non-fatal errors only, set mInterface to null to + // prevent the NetworkManagementEventObserver from killing this VPN based on the + // interface going down (which we expect). + mInterface = null; + mConfig.interfaze = null; + + // Set as unroutable to prevent traffic leaking while the interface is down. + if (mConfig != null && mConfig.routes != null) { + final List<RouteInfo> oldRoutes = new ArrayList<>(mConfig.routes); + + mConfig.routes.clear(); + for (final RouteInfo route : oldRoutes) { + mConfig.routes.add(new RouteInfo(route.getDestination(), RTN_UNREACHABLE)); + } + if (mNetworkAgent != null) { + mNetworkAgent.sendLinkProperties(makeLinkProperties()); + } + } + } + + resetIkeState(); + } + + /** + * Cleans up all IKE state + * + * <p>This method MUST always be called on the mExecutor thread in order to ensure + * consistency of the Ikev2VpnRunner fields. + */ + private void resetIkeState() { + if (mTunnelIface != null) { + // No need to call setInterfaceDown(); the IpSecInterface is being fully torn down. + mTunnelIface.close(); + mTunnelIface = null; + } + if (mSession != null) { + mSession.kill(); // Kill here to make sure all resources are released immediately + mSession = null; + } + + // TODO(b/149356682): Update this based on new IKE API + if (mEncapSocket != null) { + try { + mEncapSocket.close(); + } catch (IOException e) { + Log.e(TAG, "Failed to close encap socket", e); + } + mEncapSocket = null; + } + } + + /** + * Triggers cleanup of outer class' state + * + * <p>Can be called from any thread, as it does not mutate state in the Ikev2VpnRunner. + */ + private void cleanupVpnState() { + synchronized (Vpn.this) { + agentDisconnect(); + } + } + + /** + * Cleans up all Ikev2VpnRunner internal state + * + * <p>This method MUST always be called on the mExecutor thread in order to ensure + * consistency of the Ikev2VpnRunner fields. + */ + private void shutdownVpnRunner() { + mActiveNetwork = null; + mIsRunning = false; + + resetIkeState(); + + final ConnectivityManager cm = ConnectivityManager.from(mContext); + cm.unregisterNetworkCallback(mNetworkCallback); + + mExecutor.shutdown(); } @Override public void exit() { - // TODO: Teardown IKE session & any resources. - agentDisconnect(); + // Cleanup outer class' state immediately, otherwise race conditions may ensue. + cleanupVpnState(); + + mExecutor.execute(() -> { + shutdownVpnRunner(); + }); } } @@ -2484,12 +2846,46 @@ public class Vpn { throw new IllegalArgumentException("No profile found for " + packageName); } - startVpnProfilePrivileged(profile); + startVpnProfilePrivileged(profile, packageName); }); } - private void startVpnProfilePrivileged(@NonNull VpnProfile profile) { - // TODO: Start PlatformVpnRunner + private void startVpnProfilePrivileged( + @NonNull VpnProfile profile, @NonNull String packageName) { + // Ensure that no other previous instance is running. + if (mVpnRunner != null) { + mVpnRunner.exit(); + mVpnRunner = null; + } + updateState(DetailedState.CONNECTING, "startPlatformVpn"); + + try { + // Build basic config + mConfig = new VpnConfig(); + mConfig.user = packageName; + mConfig.isMetered = profile.isMetered; + mConfig.startTime = SystemClock.elapsedRealtime(); + mConfig.proxyInfo = profile.proxy; + + switch (profile.type) { + case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS: + case VpnProfile.TYPE_IKEV2_IPSEC_PSK: + case VpnProfile.TYPE_IKEV2_IPSEC_RSA: + mVpnRunner = new IkeV2VpnRunner(Ikev2VpnProfile.fromVpnProfile(profile)); + mVpnRunner.start(); + break; + default: + updateState(DetailedState.FAILED, "Invalid platform VPN type"); + Log.d(TAG, "Unknown VPN profile type: " + profile.type); + break; + } + } catch (IOException | GeneralSecurityException e) { + // Reset mConfig + mConfig = null; + + updateState(DetailedState.FAILED, "VPN startup failed"); + throw new IllegalArgumentException("VPN startup failed", e); + } } /** @@ -2503,13 +2899,37 @@ public class Vpn { public synchronized void stopVpnProfile(@NonNull String packageName) { checkNotNull(packageName, "No package name provided"); - // To stop the VPN profile, the caller must be the current prepared package. Otherwise, - // the app is not prepared, and we can just return. - if (!isCurrentPreparedPackage(packageName)) { - // TODO: Also check to make sure that the running VPN is a VPN profile. + // To stop the VPN profile, the caller must be the current prepared package and must be + // running an Ikev2VpnProfile. + if (!isCurrentPreparedPackage(packageName) && mVpnRunner instanceof IkeV2VpnRunner) { return; } prepareInternal(VpnConfig.LEGACY_VPN); } + + /** + * Proxy to allow testing + * + * @hide + */ + @VisibleForTesting + public static class Ikev2SessionCreator { + /** Creates a IKE session */ + public IkeSession createIkeSession( + @NonNull Context context, + @NonNull IkeSessionParams ikeSessionParams, + @NonNull ChildSessionParams firstChildSessionParams, + @NonNull Executor userCbExecutor, + @NonNull IkeSessionCallback ikeSessionCallback, + @NonNull ChildSessionCallback firstChildSessionCallback) { + return new IkeSession( + context, + ikeSessionParams, + firstChildSessionParams, + userCbExecutor, + ikeSessionCallback, + firstChildSessionCallback); + } + } } diff --git a/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java b/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java new file mode 100644 index 000000000000..33fc32b78df7 --- /dev/null +++ b/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java @@ -0,0 +1,390 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.connectivity; + +import static android.net.ConnectivityManager.NetworkCallback; +import static android.net.ipsec.ike.SaProposal.DH_GROUP_1024_BIT_MODP; +import static android.net.ipsec.ike.SaProposal.DH_GROUP_2048_BIT_MODP; +import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_CBC; +import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12; +import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16; +import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8; +import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_AES_XCBC_96; +import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96; +import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128; +import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_384_192; +import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_512_256; +import static android.net.ipsec.ike.SaProposal.KEY_LEN_AES_128; +import static android.net.ipsec.ike.SaProposal.KEY_LEN_AES_192; +import static android.net.ipsec.ike.SaProposal.KEY_LEN_AES_256; +import static android.net.ipsec.ike.SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC; +import static android.net.ipsec.ike.SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1; + +import android.annotation.NonNull; +import android.net.Ikev2VpnProfile; +import android.net.InetAddresses; +import android.net.IpPrefix; +import android.net.IpSecManager.UdpEncapsulationSocket; +import android.net.IpSecTransform; +import android.net.Network; +import android.net.RouteInfo; +import android.net.eap.EapSessionConfig; +import android.net.ipsec.ike.ChildSaProposal; +import android.net.ipsec.ike.ChildSessionCallback; +import android.net.ipsec.ike.ChildSessionConfiguration; +import android.net.ipsec.ike.ChildSessionParams; +import android.net.ipsec.ike.IkeFqdnIdentification; +import android.net.ipsec.ike.IkeIdentification; +import android.net.ipsec.ike.IkeIpv4AddrIdentification; +import android.net.ipsec.ike.IkeIpv6AddrIdentification; +import android.net.ipsec.ike.IkeKeyIdIdentification; +import android.net.ipsec.ike.IkeRfc822AddrIdentification; +import android.net.ipsec.ike.IkeSaProposal; +import android.net.ipsec.ike.IkeSessionCallback; +import android.net.ipsec.ike.IkeSessionConfiguration; +import android.net.ipsec.ike.IkeSessionParams; +import android.net.ipsec.ike.IkeTrafficSelector; +import android.net.ipsec.ike.TunnelModeChildSessionParams; +import android.net.ipsec.ike.exceptions.IkeException; +import android.net.ipsec.ike.exceptions.IkeProtocolException; +import android.net.util.IpRange; +import android.system.OsConstants; +import android.util.Log; + +import com.android.internal.net.VpnProfile; +import com.android.internal.util.HexDump; + +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; + +/** + * Utility class to build and convert IKEv2/IPsec parameters. + * + * @hide + */ +public class VpnIkev2Utils { + static IkeSessionParams buildIkeSessionParams( + @NonNull Ikev2VpnProfile profile, @NonNull UdpEncapsulationSocket socket) { + // TODO(b/149356682): Update this based on new IKE API. Only numeric addresses supported + // until then. All others throw IAE (caught by caller). + final InetAddress serverAddr = InetAddresses.parseNumericAddress(profile.getServerAddr()); + final IkeIdentification localId = parseIkeIdentification(profile.getUserIdentity()); + final IkeIdentification remoteId = parseIkeIdentification(profile.getServerAddr()); + + // TODO(b/149356682): Update this based on new IKE API. + final IkeSessionParams.Builder ikeOptionsBuilder = + new IkeSessionParams.Builder() + .setServerAddress(serverAddr) + .setUdpEncapsulationSocket(socket) + .setLocalIdentification(localId) + .setRemoteIdentification(remoteId); + setIkeAuth(profile, ikeOptionsBuilder); + + for (final IkeSaProposal ikeProposal : getIkeSaProposals()) { + ikeOptionsBuilder.addSaProposal(ikeProposal); + } + + return ikeOptionsBuilder.build(); + } + + static ChildSessionParams buildChildSessionParams() { + final TunnelModeChildSessionParams.Builder childOptionsBuilder = + new TunnelModeChildSessionParams.Builder(); + + for (final ChildSaProposal childProposal : getChildSaProposals()) { + childOptionsBuilder.addSaProposal(childProposal); + } + + childOptionsBuilder.addInternalAddressRequest(OsConstants.AF_INET); + childOptionsBuilder.addInternalAddressRequest(OsConstants.AF_INET6); + childOptionsBuilder.addInternalDnsServerRequest(OsConstants.AF_INET); + childOptionsBuilder.addInternalDnsServerRequest(OsConstants.AF_INET6); + + return childOptionsBuilder.build(); + } + + private static void setIkeAuth( + @NonNull Ikev2VpnProfile profile, @NonNull IkeSessionParams.Builder builder) { + switch (profile.getType()) { + case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS: + final EapSessionConfig eapConfig = + new EapSessionConfig.Builder() + .setEapMsChapV2Config(profile.getUsername(), profile.getPassword()) + .build(); + builder.setAuthEap(profile.getServerRootCaCert(), eapConfig); + break; + case VpnProfile.TYPE_IKEV2_IPSEC_PSK: + builder.setAuthPsk(profile.getPresharedKey()); + break; + case VpnProfile.TYPE_IKEV2_IPSEC_RSA: + builder.setAuthDigitalSignature( + profile.getServerRootCaCert(), + profile.getUserCert(), + profile.getRsaPrivateKey()); + break; + default: + throw new IllegalArgumentException("Unknown auth method set"); + } + } + + private static List<IkeSaProposal> getIkeSaProposals() { + // TODO: filter this based on allowedAlgorithms + final List<IkeSaProposal> proposals = new ArrayList<>(); + + // Encryption Algorithms: Currently only AES_CBC is supported. + final IkeSaProposal.Builder normalModeBuilder = new IkeSaProposal.Builder(); + + // Currently only AES_CBC is supported. + normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_256); + normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_192); + normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_128); + + // Authentication/Integrity Algorithms + normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_512_256); + normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_384_192); + normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128); + normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_AES_XCBC_96); + normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA1_96); + + // Add AEAD options + final IkeSaProposal.Builder aeadBuilder = new IkeSaProposal.Builder(); + aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_256); + aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_256); + aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_256); + aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_192); + aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_192); + aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_192); + aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_128); + aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_128); + aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_128); + + // Add dh, prf for both builders + for (final IkeSaProposal.Builder builder : Arrays.asList(normalModeBuilder, aeadBuilder)) { + builder.addDhGroup(DH_GROUP_2048_BIT_MODP); + builder.addDhGroup(DH_GROUP_1024_BIT_MODP); + builder.addPseudorandomFunction(PSEUDORANDOM_FUNCTION_AES128_XCBC); + builder.addPseudorandomFunction(PSEUDORANDOM_FUNCTION_HMAC_SHA1); + } + + proposals.add(normalModeBuilder.build()); + proposals.add(aeadBuilder.build()); + return proposals; + } + + private static List<ChildSaProposal> getChildSaProposals() { + // TODO: filter this based on allowedAlgorithms + final List<ChildSaProposal> proposals = new ArrayList<>(); + + // Add non-AEAD options + final ChildSaProposal.Builder normalModeBuilder = new ChildSaProposal.Builder(); + + // Encryption Algorithms: Currently only AES_CBC is supported. + normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_256); + normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_192); + normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_128); + + // Authentication/Integrity Algorithms + normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_512_256); + normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_384_192); + normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128); + normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA1_96); + + // Add AEAD options + final ChildSaProposal.Builder aeadBuilder = new ChildSaProposal.Builder(); + aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_256); + aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_256); + aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_256); + aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_192); + aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_192); + aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_192); + aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_128); + aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_128); + aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_128); + + proposals.add(normalModeBuilder.build()); + proposals.add(aeadBuilder.build()); + return proposals; + } + + static class IkeSessionCallbackImpl implements IkeSessionCallback { + private final String mTag; + private final Vpn.IkeV2VpnRunnerCallback mCallback; + private final Network mNetwork; + + IkeSessionCallbackImpl(String tag, Vpn.IkeV2VpnRunnerCallback callback, Network network) { + mTag = tag; + mCallback = callback; + mNetwork = network; + } + + @Override + public void onOpened(@NonNull IkeSessionConfiguration ikeSessionConfig) { + Log.d(mTag, "IkeOpened for network " + mNetwork); + // Nothing to do here. + } + + @Override + public void onClosed() { + Log.d(mTag, "IkeClosed for network " + mNetwork); + mCallback.onSessionLost(mNetwork); // Server requested session closure. Retry? + } + + @Override + public void onClosedExceptionally(@NonNull IkeException exception) { + Log.d(mTag, "IkeClosedExceptionally for network " + mNetwork, exception); + mCallback.onSessionLost(mNetwork); + } + + @Override + public void onError(@NonNull IkeProtocolException exception) { + Log.d(mTag, "IkeError for network " + mNetwork, exception); + // Non-fatal, log and continue. + } + } + + static class ChildSessionCallbackImpl implements ChildSessionCallback { + private final String mTag; + private final Vpn.IkeV2VpnRunnerCallback mCallback; + private final Network mNetwork; + + ChildSessionCallbackImpl(String tag, Vpn.IkeV2VpnRunnerCallback callback, Network network) { + mTag = tag; + mCallback = callback; + mNetwork = network; + } + + @Override + public void onOpened(@NonNull ChildSessionConfiguration childConfig) { + Log.d(mTag, "ChildOpened for network " + mNetwork); + mCallback.onChildOpened(mNetwork, childConfig); + } + + @Override + public void onClosed() { + Log.d(mTag, "ChildClosed for network " + mNetwork); + mCallback.onSessionLost(mNetwork); + } + + @Override + public void onClosedExceptionally(@NonNull IkeException exception) { + Log.d(mTag, "ChildClosedExceptionally for network " + mNetwork, exception); + mCallback.onSessionLost(mNetwork); + } + + @Override + public void onIpSecTransformCreated(@NonNull IpSecTransform transform, int direction) { + Log.d(mTag, "ChildTransformCreated; Direction: " + direction + "; network " + mNetwork); + mCallback.onChildTransformCreated(mNetwork, transform, direction); + } + + @Override + public void onIpSecTransformDeleted(@NonNull IpSecTransform transform, int direction) { + // Nothing to be done; no references to the IpSecTransform are held by the + // Ikev2VpnRunner (or this callback class), and this transform will be closed by the + // IKE library. + Log.d(mTag, + "ChildTransformDeleted; Direction: " + direction + "; for network " + mNetwork); + } + } + + static class Ikev2VpnNetworkCallback extends NetworkCallback { + private final String mTag; + private final Vpn.IkeV2VpnRunnerCallback mCallback; + + Ikev2VpnNetworkCallback(String tag, Vpn.IkeV2VpnRunnerCallback callback) { + mTag = tag; + mCallback = callback; + } + + @Override + public void onAvailable(@NonNull Network network) { + Log.d(mTag, "Starting IKEv2/IPsec session on new network: " + network); + mCallback.onDefaultNetworkChanged(network); + } + + @Override + public void onLost(@NonNull Network network) { + Log.d(mTag, "Tearing down; lost network: " + network); + mCallback.onSessionLost(network); + } + } + + /** + * Identity parsing logic using similar logic to open source implementations of IKEv2 + * + * <p>This method does NOT support using type-prefixes (eg 'fqdn:' or 'keyid'), or ASN.1 encoded + * identities. + */ + private static IkeIdentification parseIkeIdentification(@NonNull String identityStr) { + // TODO: Add identity formatting to public API javadocs. + if (identityStr.contains("@")) { + if (identityStr.startsWith("@#")) { + // KEY_ID + final String hexStr = identityStr.substring(2); + return new IkeKeyIdIdentification(HexDump.hexStringToByteArray(hexStr)); + } else if (identityStr.startsWith("@@")) { + // RFC822 (USER_FQDN) + return new IkeRfc822AddrIdentification(identityStr.substring(2)); + } else if (identityStr.startsWith("@")) { + // FQDN + return new IkeFqdnIdentification(identityStr.substring(1)); + } else { + // RFC822 (USER_FQDN) + return new IkeRfc822AddrIdentification(identityStr); + } + } else if (InetAddresses.isNumericAddress(identityStr)) { + final InetAddress addr = InetAddresses.parseNumericAddress(identityStr); + if (addr instanceof Inet4Address) { + // IPv4 + return new IkeIpv4AddrIdentification((Inet4Address) addr); + } else if (addr instanceof Inet6Address) { + // IPv6 + return new IkeIpv6AddrIdentification((Inet6Address) addr); + } else { + throw new IllegalArgumentException("IP version not supported"); + } + } else { + if (identityStr.contains(":")) { + // KEY_ID + return new IkeKeyIdIdentification(identityStr.getBytes()); + } else { + // FQDN + return new IkeFqdnIdentification(identityStr); + } + } + } + + static Collection<RouteInfo> getRoutesFromTrafficSelectors( + List<IkeTrafficSelector> trafficSelectors) { + final HashSet<RouteInfo> routes = new HashSet<>(); + + for (final IkeTrafficSelector selector : trafficSelectors) { + for (final IpPrefix prefix : + new IpRange(selector.startingAddress, selector.endingAddress).asIpPrefixes()) { + routes.add(new RouteInfo(prefix, null)); + } + } + + return routes; + } +} diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java index b24a938ef7ea..563dcf7e1156 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java @@ -130,7 +130,7 @@ public abstract class NetworkPolicyManagerInternal { Set<String> packageNames, int userId); /** - * Notifies that any of the {@link AbstractNetworkStatsProvider} has reached its quota + * Notifies that the specified {@link AbstractNetworkStatsProvider} has reached its quota * which was set through {@link AbstractNetworkStatsProvider#setLimit(String, long)}. * * @param tag the human readable identifier of the custom network stats provider. diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 9760185ca6df..10cf250acb14 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -4613,13 +4613,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final long quota = ((long) msg.arg1 << 32) | (msg.arg2 & 0xFFFFFFFFL); removeInterfaceQuota(iface); setInterfaceQuota(iface, quota); - mNetworkStats.setStatsProviderLimit(iface, quota); + mNetworkStats.setStatsProviderLimitAsync(iface, quota); return true; } case MSG_REMOVE_INTERFACE_QUOTA: { final String iface = (String) msg.obj; removeInterfaceQuota(iface); - mNetworkStats.setStatsProviderLimit(iface, QUOTA_UNLIMITED); + mNetworkStats.setStatsProviderLimitAsync(iface, QUOTA_UNLIMITED); return true; } case MSG_RESET_FIREWALL_RULES_BY_UID: { diff --git a/services/core/java/com/android/server/net/NetworkStatsManagerInternal.java b/services/core/java/com/android/server/net/NetworkStatsManagerInternal.java index 6d72cb5ee345..0cb0bc2c0896 100644 --- a/services/core/java/com/android/server/net/NetworkStatsManagerInternal.java +++ b/services/core/java/com/android/server/net/NetworkStatsManagerInternal.java @@ -40,5 +40,5 @@ public abstract class NetworkStatsManagerInternal { * Set the quota limit to all registered custom network stats providers. * Note that invocation of any interface will be sent to all providers. */ - public abstract void setStatsProviderLimit(@NonNull String iface, long quota); + public abstract void setStatsProviderLimitAsync(@NonNull String iface, long quota); } diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java index 415ccb8fe877..a7f861836202 100644 --- a/services/core/java/com/android/server/net/NetworkStatsService.java +++ b/services/core/java/com/android/server/net/NetworkStatsService.java @@ -155,6 +155,8 @@ import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Objects; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; /** * Collect and persist detailed network statistics, and provide this data to @@ -255,7 +257,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } private final Object mStatsLock = new Object(); - private final Object mStatsProviderLock = new Object(); /** Set of currently active ifaces. */ @GuardedBy("mStatsLock") @@ -280,8 +281,11 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private final DropBoxNonMonotonicObserver mNonMonotonicObserver = new DropBoxNonMonotonicObserver(); + private static final int MAX_STATS_PROVIDER_POLL_WAIT_TIME_MS = 100; private final RemoteCallbackList<NetworkStatsProviderCallbackImpl> mStatsProviderCbList = new RemoteCallbackList<>(); + /** Semaphore used to wait for stats provider to respond to request stats update. */ + private final Semaphore mStatsProviderSem = new Semaphore(0, true); @GuardedBy("mStatsLock") private NetworkStatsRecorder mDevRecorder; @@ -1337,6 +1341,25 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final boolean persistUid = (flags & FLAG_PERSIST_UID) != 0; final boolean persistForce = (flags & FLAG_PERSIST_FORCE) != 0; + // Request asynchronous stats update from all providers for next poll. And wait a bit of + // time to allow providers report-in given that normally binder call should be fast. + // TODO: request with a valid token. + Trace.traceBegin(TRACE_TAG_NETWORK, "provider.requestStatsUpdate"); + final int registeredCallbackCount = mStatsProviderCbList.getRegisteredCallbackCount(); + mStatsProviderSem.drainPermits(); + invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.requestStatsUpdate(0 /* unused */)); + try { + mStatsProviderSem.tryAcquire(registeredCallbackCount, + MAX_STATS_PROVIDER_POLL_WAIT_TIME_MS, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + // Strictly speaking it's possible a provider happened to deliver between the timeout + // and the log, and that doesn't matter too much as this is just a debug log. + Log.d(TAG, "requestStatsUpdate - providers responded " + + mStatsProviderSem.availablePermits() + + "/" + registeredCallbackCount + " : " + e); + } + Trace.traceEnd(TRACE_TAG_NETWORK); + // TODO: consider marking "untrusted" times in historical stats final long currentTime = mClock.millis(); @@ -1374,10 +1397,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { performSampleLocked(); } - // request asynchronous stats update from all providers for next poll. - // TODO: request with a valid token. - invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.requestStatsUpdate(0 /* unused */)); - // finally, dispatch updated event to any listeners final Intent updatedIntent = new Intent(ACTION_NETWORK_STATS_UPDATED); updatedIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); @@ -1501,8 +1520,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } @Override - public void setStatsProviderLimit(@NonNull String iface, long quota) { - Slog.v(TAG, "setStatsProviderLimit(" + iface + "," + quota + ")"); + public void setStatsProviderLimitAsync(@NonNull String iface, long quota) { + Slog.v(TAG, "setStatsProviderLimitAsync(" + iface + "," + quota + ")"); invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.setLimit(iface, quota)); } } @@ -1783,9 +1802,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub { * {@code unregister()} of the returned callback. * * @param tag a human readable identifier of the custom network stats provider. - * @param provider the binder interface of - * {@link android.net.netstats.provider.AbstractNetworkStatsProvider} that - * needs to be registered to the system. + * @param provider the {@link INetworkStatsProvider} binder corresponding to the + * {@link android.net.netstats.provider.AbstractNetworkStatsProvider} to be + * registered. * * @return a binder interface of * {@link android.net.netstats.provider.NetworkStatsProviderCallback}, which can be @@ -1798,7 +1817,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub { Objects.requireNonNull(tag, "tag is null"); try { NetworkStatsProviderCallbackImpl callback = new NetworkStatsProviderCallbackImpl( - tag, provider, mAlertObserver, mStatsProviderCbList); + tag, provider, mStatsProviderSem, mAlertObserver, + mStatsProviderCbList); mStatsProviderCbList.register(callback); Log.d(TAG, "registerNetworkStatsProvider from " + callback.mTag + " uid/pid=" + getCallingUid() + "/" + getCallingPid()); @@ -1823,7 +1843,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private void invokeForAllStatsProviderCallbacks( @NonNull ThrowingConsumer<NetworkStatsProviderCallbackImpl, RemoteException> task) { - synchronized (mStatsProviderCbList) { + synchronized (mStatsLock) { final int length = mStatsProviderCbList.beginBroadcast(); try { for (int i = 0; i < length; i++) { @@ -1844,25 +1864,30 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private static class NetworkStatsProviderCallbackImpl extends INetworkStatsProviderCallback.Stub implements IBinder.DeathRecipient { @NonNull final String mTag; - @NonNull private final Object mProviderStatsLock = new Object(); + @NonNull final INetworkStatsProvider mProvider; + @NonNull private final Semaphore mSemaphore; @NonNull final INetworkManagementEventObserver mAlertObserver; @NonNull final RemoteCallbackList<NetworkStatsProviderCallbackImpl> mStatsProviderCbList; + @NonNull private final Object mProviderStatsLock = new Object(); + @GuardedBy("mProviderStatsLock") - // STATS_PER_IFACE and STATS_PER_UID + // Track STATS_PER_IFACE and STATS_PER_UID separately. private final NetworkStats mIfaceStats = new NetworkStats(0L, 0); @GuardedBy("mProviderStatsLock") private final NetworkStats mUidStats = new NetworkStats(0L, 0); NetworkStatsProviderCallbackImpl( @NonNull String tag, @NonNull INetworkStatsProvider provider, + @NonNull Semaphore semaphore, @NonNull INetworkManagementEventObserver alertObserver, @NonNull RemoteCallbackList<NetworkStatsProviderCallbackImpl> cbList) throws RemoteException { mTag = tag; mProvider = provider; mProvider.asBinder().linkToDeath(this, 0); + mSemaphore = semaphore; mAlertObserver = alertObserver; mStatsProviderCbList = cbList; } @@ -1881,7 +1906,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub { default: throw new IllegalArgumentException("Invalid type: " + how); } - // Return a defensive copy instead of local reference. + // Callers might be able to mutate the returned object. Return a defensive copy + // instead of local reference. return stats.clone(); } } @@ -1895,6 +1921,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { if (ifaceStats != null) mIfaceStats.combineAllValues(ifaceStats); if (uidStats != null) mUidStats.combineAllValues(uidStats); } + mSemaphore.release(); } @Override diff --git a/services/core/xsd/vts/Android.bp b/services/core/xsd/vts/Android.bp index 5545656a36de..636d11069731 100644 --- a/services/core/xsd/vts/Android.bp +++ b/services/core/xsd/vts/Android.bp @@ -31,4 +31,12 @@ cc_test { "-Wall", "-Werror", ], + data: [ + ":default-permissions", + ], + test_suites: [ + "general-tests", + "vts-core" + ], + test_config: "vts_defaultPermissions_validate_test.xml", } diff --git a/services/core/xsd/vts/vts_defaultPermissions_validate_test.xml b/services/core/xsd/vts/vts_defaultPermissions_validate_test.xml new file mode 100644 index 000000000000..1ccacae12969 --- /dev/null +++ b/services/core/xsd/vts/vts_defaultPermissions_validate_test.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<configuration description="Runs vts_defaultPermissions_validate_test."> + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="apct-native" /> + + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"> + </target_preparer> + + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="cleanup" value="true" /> + <option name="push" value="default-permissions.xsd->/data/local/tmp/default-permissions.xsd" /> + <option name="push" value="vts_defaultPermissions_validate_test->/data/local/tmp/vts_defaultPermissions_validate_test" /> + </target_preparer> + + <test class="com.android.tradefed.testtype.GTest" > + <option name="native-test-device-path" value="/data/local/tmp" /> + <option name="module-name" value="vts_defaultPermissions_validate_test" /> + </test> +</configuration> diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java index 2e58ad68fc7c..ece937a57247 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java @@ -1702,7 +1702,7 @@ public class NetworkPolicyManagerServiceTest { // Get active mobile network in place expectMobileDefaults(); mService.updateNetworks(); - verify(mStatsService).setStatsProviderLimit(TEST_IFACE, Long.MAX_VALUE); + verify(mStatsService).setStatsProviderLimitAsync(TEST_IFACE, Long.MAX_VALUE); // Set limit to 10KB. setNetworkPolicies(new NetworkPolicy( @@ -1711,7 +1711,7 @@ public class NetworkPolicyManagerServiceTest { postMsgAndWaitForCompletion(); // Verifies that remaining quota is set to providers. - verify(mStatsService).setStatsProviderLimit(TEST_IFACE, 10000L - 4999L); + verify(mStatsService).setStatsProviderLimitAsync(TEST_IFACE, 10000L - 4999L); reset(mStatsService); @@ -1733,7 +1733,7 @@ public class NetworkPolicyManagerServiceTest { postMsgAndWaitForCompletion(); verify(mStatsService).forceUpdate(); postMsgAndWaitForCompletion(); - verify(mStatsService).setStatsProviderLimit(TEST_IFACE, 10000L - 4999L - 1999L); + verify(mStatsService).setStatsProviderLimitAsync(TEST_IFACE, 10000L - 4999L - 1999L); } /** diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java index ec99f36f6e70..52213d8c4fae 100644 --- a/telecomm/java/android/telecom/Call.java +++ b/telecomm/java/android/telecom/Call.java @@ -17,6 +17,7 @@ package android.telecom; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; @@ -458,8 +459,14 @@ public final class Call { /** Call supports the deflect feature. */ public static final int CAPABILITY_SUPPORT_DEFLECT = 0x01000000; + /** + * Call supports adding participants to the call via + * {@link #addConferenceParticipants(List)}. + * @hide + */ + public static final int CAPABILITY_ADD_PARTICIPANT = 0x02000000; //****************************************************************************************** - // Next CAPABILITY value: 0x02000000 + // Next CAPABILITY value: 0x04000000 //****************************************************************************************** /** @@ -539,7 +546,7 @@ public final class Call { * * @see TelecomManager#EXTRA_USE_ASSISTED_DIALING */ - public static final int PROPERTY_ASSISTED_DIALING_USED = 0x00000200; + public static final int PROPERTY_ASSISTED_DIALING = 0x00000200; /** * Indicates that the call is an RTT call. Use {@link #getRttCall()} to get the @@ -689,6 +696,9 @@ public final class Call { if (can(capabilities, CAPABILITY_SUPPORT_DEFLECT)) { builder.append(" CAPABILITY_SUPPORT_DEFLECT"); } + if (can(capabilities, CAPABILITY_ADD_PARTICIPANT)) { + builder.append(" CAPABILITY_ADD_PARTICIPANT"); + } builder.append("]"); return builder.toString(); } @@ -744,7 +754,7 @@ public final class Call { if (hasProperty(properties, PROPERTY_HAS_CDMA_VOICE_PRIVACY)) { builder.append(" PROPERTY_HAS_CDMA_VOICE_PRIVACY"); } - if (hasProperty(properties, PROPERTY_ASSISTED_DIALING_USED)) { + if (hasProperty(properties, PROPERTY_ASSISTED_DIALING)) { builder.append(" PROPERTY_ASSISTED_DIALING_USED"); } if (hasProperty(properties, PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL)) { @@ -1703,6 +1713,17 @@ public final class Call { } /** + * Pulls participants to existing call by forming a conference call. + * See {@link Details#CAPABILITY_ADD_PARTICIPANT}. + * + * @param participants participants to be pulled to existing call. + * @hide + */ + public void addConferenceParticipants(@NonNull List<Uri> participants) { + mInCallAdapter.addConferenceParticipants(mTelecomCallId, participants); + } + + /** * Initiates a request to the {@link ConnectionService} to pull an external call to the local * device. * <p> diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java index 56acdff530eb..f019a9d33005 100644 --- a/telecomm/java/android/telecom/Conference.java +++ b/telecomm/java/android/telecom/Conference.java @@ -16,8 +16,13 @@ package android.telecom; +import static android.Manifest.permission.MODIFY_PHONE_STATE; + +import android.annotation.ElapsedRealtimeLong; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.TestApi; import android.net.Uri; @@ -319,6 +324,13 @@ public abstract class Conference extends Conferenceable { public void onConnectionAdded(Connection connection) {} /** + * Notifies the {@link Conference} of a request to add a new participants to the conference call + * @param participants that will be added to this conference call + * @hide + */ + public void onAddConferenceParticipants(@NonNull List<Uri> participants) {} + + /** * Notifies this Conference, which is in {@code STATE_RINGING}, of * a request to accept. * For managed {@link ConnectionService}s, this will be called when the user answers a call via @@ -625,12 +637,12 @@ public abstract class Conference extends Conferenceable { * Should be specified in wall-clock time returned by {@link System#currentTimeMillis()}. * <p> * When setting the connection time, you should always set the connection elapsed time via - * {@link #setConnectionStartElapsedRealTime(long)} to ensure the duration is reflected. + * {@link #setConnectionStartElapsedRealtimeMillis(long)} to ensure the duration is reflected. * * @param connectionTimeMillis The connection time, in milliseconds, as returned by * {@link System#currentTimeMillis()}. */ - public final void setConnectionTime(long connectionTimeMillis) { + public final void setConnectionTime(@IntRange(from = 0) long connectionTimeMillis) { mConnectTimeMillis = connectionTimeMillis; } @@ -646,8 +658,28 @@ public abstract class Conference extends Conferenceable { * * @param connectionStartElapsedRealTime The connection time, as measured by * {@link SystemClock#elapsedRealtime()}. + * @deprecated use {@link #setConnectionStartElapsedRealtimeMillis(long)} instead. */ + @Deprecated public final void setConnectionStartElapsedRealTime(long connectionStartElapsedRealTime) { + setConnectionStartElapsedRealtimeMillis(connectionStartElapsedRealTime); + } + + /** + * Sets the start time of the {@link Conference} which is the basis for the determining the + * duration of the {@link Conference}. + * <p> + * You should use a value returned by {@link SystemClock#elapsedRealtime()} to ensure that time + * zone changes do not impact the conference duration. + * <p> + * When setting this, you should also set the connection time via + * {@link #setConnectionTime(long)}. + * + * @param connectionStartElapsedRealTime The connection time, as measured by + * {@link SystemClock#elapsedRealtime()}. + */ + public final void setConnectionStartElapsedRealtimeMillis( + @ElapsedRealtimeLong long connectionStartElapsedRealTime) { mConnectionStartElapsedRealTime = connectionStartElapsedRealTime; } @@ -668,7 +700,7 @@ public abstract class Conference extends Conferenceable { * * @return The time at which the {@code Conference} was connected. */ - public final long getConnectionTime() { + public final @IntRange(from = 0) long getConnectionTime() { return mConnectTimeMillis; } @@ -685,11 +717,8 @@ public abstract class Conference extends Conferenceable { * has no general use other than to the Telephony framework. * * @return The elapsed time at which the {@link Conference} was connected. - * @hide */ - @SystemApi - @TestApi - public final long getConnectionStartElapsedRealTime() { + public final @ElapsedRealtimeLong long getConnectionStartElapsedRealtimeMillis() { return mConnectionStartElapsedRealTime; } @@ -987,6 +1016,7 @@ public abstract class Conference extends Conferenceable { */ @SystemApi @TestApi + @RequiresPermission(MODIFY_PHONE_STATE) public void setConferenceState(boolean isConference) { for (Listener l : mListeners) { l.onConferenceStateChanged(this, isConference); @@ -1007,6 +1037,7 @@ public abstract class Conference extends Conferenceable { */ @SystemApi @TestApi + @RequiresPermission(MODIFY_PHONE_STATE) public final void setAddress(@NonNull Uri address, @TelecomManager.Presentation int presentation) { Log.d(this, "setAddress %s", address); @@ -1113,12 +1144,52 @@ public abstract class Conference extends Conferenceable { } /** - * Sends an event associated with this {@code Conference} with associated event extras to the - * {@link InCallService} (note: this is identical in concept to - * {@link Connection#sendConnectionEvent(String, Bundle)}). - * @see Connection#sendConnectionEvent(String, Bundle) + * Sends an event associated with this {@link Conference} with associated event extras to the + * {@link InCallService}. + * <p> + * Connection events are used to communicate point in time information from a + * {@link ConnectionService} to an {@link InCallService} implementation. An example of a + * custom connection event includes notifying the UI when a WIFI call has been handed over to + * LTE, which the InCall UI might use to inform the user that billing charges may apply. The + * Android Telephony framework will send the {@link Connection#EVENT_MERGE_COMPLETE} + * connection event when a call to {@link Call#mergeConference()} has completed successfully. + * <p> + * Events are exposed to {@link InCallService} implementations via + * {@link Call.Callback#onConnectionEvent(Call, String, Bundle)}. + * <p> + * No assumptions should be made as to how an In-Call UI or service will handle these events. + * The {@link ConnectionService} must assume that the In-Call UI could even chose to ignore + * some events altogether. + * <p> + * Events should be fully qualified (e.g. {@code com.example.event.MY_EVENT}) to avoid + * conflicts between {@link ConnectionService} implementations. Further, custom + * {@link ConnectionService} implementations shall not re-purpose events in the + * {@code android.*} namespace, nor shall they define new event types in this namespace. When + * defining a custom event type, ensure the contents of the extras {@link Bundle} is clearly + * defined. Extra keys for this bundle should be named similar to the event type (e.g. + * {@code com.example.extra.MY_EXTRA}). + * <p> + * When defining events and the associated extras, it is important to keep their behavior + * consistent when the associated {@link ConnectionService} is updated. Support for deprecated + * events/extras should me maintained to ensure backwards compatibility with older + * {@link InCallService} implementations which were built to support the older behavior. + * <p> + * Expected connection events from the Telephony stack are: + * <p> + * <ul> + * <li>{@link Connection#EVENT_CALL_HOLD_FAILED} with {@code null} {@code extras} when the + * {@link Conference} could not be held.</li> + * <li>{@link Connection#EVENT_MERGE_START} with {@code null} {@code extras} when a new + * call is being merged into the conference.</li> + * <li>{@link Connection#EVENT_MERGE_COMPLETE} with {@code null} {@code extras} a new call + * has completed being merged into the conference.</li> + * <li>{@link Connection#EVENT_CALL_MERGE_FAILED} with {@code null} {@code extras} a new + * call has failed to merge into the conference (the dialer app can determine which call + * failed to merge based on the fact that the call still exists outside of the conference + * at the end of the merge process).</li> + * </ul> * - * @param event The connection event. + * @param event The conference event. * @param extras Optional bundle containing extra information associated with the event. */ public void sendConferenceEvent(@NonNull String event, @Nullable Bundle extras) { diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index 8049459cf3f4..3b0ba2548660 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -16,9 +16,14 @@ package android.telecom; +import static android.Manifest.permission.MODIFY_PHONE_STATE; + +import android.annotation.ElapsedRealtimeLong; import android.annotation.IntDef; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.TestApi; import android.app.Notification; @@ -376,8 +381,14 @@ public abstract class Connection extends Conferenceable { /** Call supports the deflect feature. */ public static final int CAPABILITY_SUPPORT_DEFLECT = 0x02000000; + /** + * When set, indicates that this {@link Connection} supports initiation of a conference call + * by directly adding participants using {@link #onAddConferenceParticipants(List)}. + * @hide + */ + public static final int CAPABILITY_ADD_PARTICIPANT = 0x04000000; //********************************************************************************************** - // Next CAPABILITY value: 0x04000000 + // Next CAPABILITY value: 0x08000000 //********************************************************************************************** /** @@ -474,7 +485,7 @@ public abstract class Connection extends Conferenceable { * * @see TelecomManager#EXTRA_USE_ASSISTED_DIALING */ - public static final int PROPERTY_ASSISTED_DIALING_USED = 1 << 9; + public static final int PROPERTY_ASSISTED_DIALING = 1 << 9; /** * Set by the framework to indicate that the network has identified a Connection as an emergency @@ -953,7 +964,9 @@ public abstract class Connection extends Conferenceable { if ((capabilities & CAPABILITY_SUPPORT_DEFLECT) == CAPABILITY_SUPPORT_DEFLECT) { builder.append(isLong ? " CAPABILITY_SUPPORT_DEFLECT" : " sup_def"); } - + if ((capabilities & CAPABILITY_ADD_PARTICIPANT) == CAPABILITY_ADD_PARTICIPANT) { + builder.append(isLong ? " CAPABILITY_ADD_PARTICIPANT" : " add_participant"); + } builder.append("]"); return builder.toString(); } @@ -2109,19 +2122,24 @@ public abstract class Connection extends Conferenceable { */ @SystemApi @TestApi - public final long getConnectTimeMillis() { + public final @IntRange(from = 0) long getConnectTimeMillis() { return mConnectTimeMillis; } /** * Retrieves the connection start time of the {@link Connection}, if specified. A value of * {@link Conference#CONNECT_TIME_NOT_SPECIFIED} indicates that Telecom should determine the - * start time of the conference. + * start time of the connection. * <p> * Based on the value of {@link SystemClock#elapsedRealtime()}, which ensures that wall-clock * changes do not impact the call duration. * <p> * Used internally in Telephony when migrating conference participant data for IMS conferences. + * <p> + * The value returned is the same one set using + * {@link #setConnectionStartElapsedRealtimeMillis(long)}. This value is never updated from + * the Telecom framework, so no permission enforcement occurs when retrieving the value with + * this method. * * @return The time at which the {@link Connection} was connected. * @@ -2129,7 +2147,7 @@ public abstract class Connection extends Conferenceable { */ @SystemApi @TestApi - public final long getConnectElapsedTimeMillis() { + public final @ElapsedRealtimeLong long getConnectionStartElapsedRealtimeMillis() { return mConnectElapsedTimeMillis; } @@ -2550,6 +2568,9 @@ public abstract class Connection extends Conferenceable { * Sets the time at which a call became active on this Connection. This is set only * when a conference call becomes active on this connection. * <p> + * This time corresponds to the date/time of connection and is stored in the call log in + * {@link android.provider.CallLog.Calls#DATE}. + * <p> * Used by telephony to maintain calls associated with an IMS Conference. * * @param connectTimeMillis The connection time, in milliseconds. Should be set using a value @@ -2559,7 +2580,8 @@ public abstract class Connection extends Conferenceable { */ @SystemApi @TestApi - public final void setConnectTimeMillis(long connectTimeMillis) { + @RequiresPermission(MODIFY_PHONE_STATE) + public final void setConnectTimeMillis(@IntRange(from = 0) long connectTimeMillis) { mConnectTimeMillis = connectTimeMillis; } @@ -2567,15 +2589,23 @@ public abstract class Connection extends Conferenceable { * Sets the time at which a call became active on this Connection. This is set only * when a conference call becomes active on this connection. * <p> + * This time is used to establish the duration of a call. It uses + * {@link SystemClock#elapsedRealtime()} to ensure that the call duration is not impacted by + * time zone changes during a call. The difference between the current + * {@link SystemClock#elapsedRealtime()} and the value set at the connection start time is used + * to populate {@link android.provider.CallLog.Calls#DURATION} in the call log. + * <p> * Used by telephony to maintain calls associated with an IMS Conference. + * * @param connectElapsedTimeMillis The connection time, in milliseconds. Stored in the format * {@link SystemClock#elapsedRealtime()}. - * * @hide */ @SystemApi @TestApi - public final void setConnectionStartElapsedRealTime(long connectElapsedTimeMillis) { + @RequiresPermission(MODIFY_PHONE_STATE) + public final void setConnectionStartElapsedRealtimeMillis( + @ElapsedRealtimeLong long connectElapsedTimeMillis) { mConnectElapsedTimeMillis = connectElapsedTimeMillis; } @@ -2953,6 +2983,14 @@ public abstract class Connection extends Conferenceable { public void onSeparate() {} /** + * Supports initiation of a conference call by directly adding participants to an ongoing call. + * + * @param participants with which conference call will be formed. + * @hide + */ + public void onAddConferenceParticipants(@NonNull List<Uri> participants) {} + + /** * Notifies this Connection of a request to abort. */ public void onAbort() {} diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java index 00c2918837ac..2aea723cf418 100644 --- a/telecomm/java/android/telecom/ConnectionService.java +++ b/telecomm/java/android/telecom/ConnectionService.java @@ -18,7 +18,6 @@ package android.telecom; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SystemApi; import android.app.Service; @@ -142,6 +141,7 @@ public abstract class ConnectionService extends Service { private static final String SESSION_SPLIT_CONFERENCE = "CS.sFC"; private static final String SESSION_MERGE_CONFERENCE = "CS.mC"; private static final String SESSION_SWAP_CONFERENCE = "CS.sC"; + private static final String SESSION_ADD_PARTICIPANT = "CS.aP"; private static final String SESSION_POST_DIAL_CONT = "CS.oPDC"; private static final String SESSION_PULL_EXTERNAL_CALL = "CS.pEC"; private static final String SESSION_SEND_CALL_EVENT = "CS.sCE"; @@ -195,6 +195,7 @@ public abstract class ConnectionService extends Service { private static final int MSG_CREATE_CONFERENCE_COMPLETE = 36; private static final int MSG_CREATE_CONFERENCE_FAILED = 37; private static final int MSG_REJECT_WITH_REASON = 38; + private static final int MSG_ADD_PARTICIPANT = 39; private static Connection sNullConnection; @@ -627,6 +628,21 @@ public abstract class ConnectionService extends Service { } @Override + public void addConferenceParticipants(String callId, List<Uri> participants, + Session.Info sessionInfo) { + Log.startSession(sessionInfo, SESSION_ADD_PARTICIPANT); + try { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = callId; + args.arg2 = participants; + args.arg3 = Log.createSubsession(); + mHandler.obtainMessage(MSG_ADD_PARTICIPANT, args).sendToTarget(); + } finally { + Log.endSession(); + } + } + + @Override public void onPostDialContinue(String callId, boolean proceed, Session.Info sessionInfo) { Log.startSession(sessionInfo, SESSION_POST_DIAL_CONT); try { @@ -1224,6 +1240,19 @@ public abstract class ConnectionService extends Service { } break; } + case MSG_ADD_PARTICIPANT: { + SomeArgs args = (SomeArgs) msg.obj; + try { + Log.continueSession((Session) args.arg3, + SESSION_HANDLER + SESSION_ADD_PARTICIPANT); + addConferenceParticipants((String) args.arg1, (List<Uri>)args.arg2); + } finally { + args.recycle(); + Log.endSession(); + } + break; + } + case MSG_ON_POST_DIAL_CONTINUE: { SomeArgs args = (SomeArgs) msg.obj; try { @@ -1778,7 +1807,7 @@ public abstract class ConnectionService extends Service { null : conference.getVideoProvider().getInterface(), conference.getVideoState(), conference.getConnectTimeMillis(), - conference.getConnectionStartElapsedRealTime(), + conference.getConnectionStartElapsedRealtimeMillis(), conference.getStatusHints(), conference.getExtras(), conference.getAddress(), @@ -1884,7 +1913,7 @@ public abstract class ConnectionService extends Service { connection.isRingbackRequested(), connection.getAudioModeIsVoip(), connection.getConnectTimeMillis(), - connection.getConnectElapsedTimeMillis(), + connection.getConnectionStartElapsedRealtimeMillis(), connection.getStatusHints(), connection.getDisconnectCause(), createIdList(connection.getConferenceables()), @@ -2152,6 +2181,17 @@ public abstract class ConnectionService extends Service { } } + private void addConferenceParticipants(String callId, List<Uri> participants) { + Log.d(this, "addConferenceParticipants(%s)", callId); + if (mConnectionById.containsKey(callId)) { + findConnectionForAction(callId, "addConferenceParticipants") + .onAddConferenceParticipants(participants); + } else { + findConferenceForAction(callId, "addConferenceParticipants") + .onAddConferenceParticipants(participants); + } + } + /** * Notifies a {@link Connection} of a request to pull an external call. * @@ -2374,7 +2414,7 @@ public abstract class ConnectionService extends Service { null : conference.getVideoProvider().getInterface(), conference.getVideoState(), conference.getConnectTimeMillis(), - conference.getConnectionStartElapsedRealTime(), + conference.getConnectionStartElapsedRealtimeMillis(), conference.getStatusHints(), conference.getExtras(), conference.getAddress(), @@ -2465,7 +2505,7 @@ public abstract class ConnectionService extends Service { connection.isRingbackRequested(), connection.getAudioModeIsVoip(), connection.getConnectTimeMillis(), - connection.getConnectElapsedTimeMillis(), + connection.getConnectionStartElapsedRealtimeMillis(), connection.getStatusHints(), connection.getDisconnectCause(), emptyList, diff --git a/telecomm/java/android/telecom/InCallAdapter.java b/telecomm/java/android/telecom/InCallAdapter.java index 594c1eb392b3..9d29174059ad 100644 --- a/telecomm/java/android/telecom/InCallAdapter.java +++ b/telecomm/java/android/telecom/InCallAdapter.java @@ -283,6 +283,20 @@ public final class InCallAdapter { } /** + * Instructs Telecom to pull participants to existing call + * + * @param callId The unique ID of the call. + * @param participants participants to be pulled to existing call. + */ + public void addConferenceParticipants(String callId, List<Uri> participants) { + try { + mAdapter.addConferenceParticipants(callId, participants); + } catch (RemoteException ignored) { + } + } + + + /** * Instructs Telecom to split the specified call from any conference call with which it may be * connected. * diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java index ebfa3a15639a..982e5f30e28c 100644 --- a/telecomm/java/android/telecom/InCallService.java +++ b/telecomm/java/android/telecom/InCallService.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.SdkConstant; import android.annotation.SystemApi; import android.app.Service; +import android.app.UiModeManager; import android.bluetooth.BluetoothDevice; import android.content.Intent; import android.hardware.camera2.CameraManager; @@ -43,12 +44,32 @@ import java.util.List; * phone calls. * <h2>Becoming the Default Phone App</h2> * The default dialer/phone app is one which provides the in-call user interface while the device is - * in a call. A device is bundled with a system provided default dialer/phone app. The user may - * choose a single app to take over this role from the system app. An app which wishes to fulfill - * one this role uses the {@code android.app.role.RoleManager} to request that they fill the role. + * in a call. It also provides the user with a means to initiate calls and see a history of calls + * on their device. A device is bundled with a system provided default dialer/phone app. The user + * may choose a single app to take over this role from the system app. An app which wishes to + * fulfill one this role uses the {@link android.app.role.RoleManager} to request that they fill the + * {@link android.app.role.RoleManager#ROLE_DIALER} role. * <p> - * An app filling the role of the default phone app provides a user interface while the device is in - * a call, and the device is not in car mode. + * The default phone app provides a user interface while the device is in a call, and the device is + * not in car mode (i.e. {@link UiModeManager#getCurrentModeType()} is not + * {@link android.content.res.Configuration#UI_MODE_TYPE_CAR}). + * <p> + * In order to fill the {@link android.app.role.RoleManager#ROLE_DIALER} role, an app must meet a + * number of requirements: + * <ul> + * <li>It must handle the {@link Intent#ACTION_DIAL} intent. This means the app must provide + * a dial pad UI for the user to initiate outgoing calls.</li> + * <li>It must fully implement the {@link InCallService} API and provide both an incoming call + * UI, as well as an ongoing call UI.</li> + * </ul> + * <p> + * Note: If the app filling the {@link android.app.role.RoleManager#ROLE_DIALER} crashes during + * {@link InCallService} binding, the Telecom framework will automatically fall back to using the + * dialer app pre-loaded on the device. The system will display a notification to the user to let + * them know that the app has crashed and that their call was continued using the pre-loaded dialer + * app. + * <p> + * Further, the pre-loaded dialer will ALWAYS be used when the user places an emergency call. * <p> * Below is an example manifest registration for an {@code InCallService}. The meta-data * {@link TelecomManager#METADATA_IN_CALL_SERVICE_UI} indicates that this particular @@ -82,6 +103,11 @@ import java.util.List; * <action android:name="android.intent.action.DIAL" /> * <category android:name="android.intent.category.DEFAULT" /> * </intent-filter> + * <intent-filter> + * <action android:name="android.intent.action.DIAL" /> + * <category android:name="android.intent.category.DEFAULT" /> + * <data android:scheme="tel" /> + * </intent-filter> * </activity> * } * </pre> @@ -111,6 +137,7 @@ import java.util.List; * } * } * } + * } * </pre> * <p id="incomingCallNotification"> * <h3>Showing the Incoming Call Notification</h3> diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java index abb210f13376..6ae4a08abaa3 100644 --- a/telecomm/java/android/telecom/PhoneAccount.java +++ b/telecomm/java/android/telecom/PhoneAccount.java @@ -16,7 +16,10 @@ package android.telecom; +import static android.Manifest.permission.MODIFY_PHONE_STATE; + import android.annotation.NonNull; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.TestApi; import android.content.Intent; @@ -605,7 +608,8 @@ public final class PhoneAccount implements Parcelable { * time. By default, there is no group Id for a {@link PhoneAccount} (an empty String). Only * grouped {@link PhoneAccount}s with the same {@link ConnectionService} can be replaced. * <p> - * Note: This is an API specific to the Telephony stack. + * Note: This is an API specific to the Telephony stack; the group Id will be ignored for + * callers not holding the correct permission. * * @param groupId The group Id of the {@link PhoneAccount} that will replace any other * registered {@link PhoneAccount} in Telecom with the same Group Id. @@ -614,6 +618,7 @@ public final class PhoneAccount implements Parcelable { */ @SystemApi @TestApi + @RequiresPermission(MODIFY_PHONE_STATE) public @NonNull Builder setGroupId(@NonNull String groupId) { if (groupId != null) { mGroupId = groupId; diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index 7e5fd461fafb..a7024f982247 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -802,8 +802,8 @@ public class TelecomManager { * automatically add dialing prefixes when placing international calls. * <p> * Setting this extra on the outgoing call extras will cause the - * {@link Connection#PROPERTY_ASSISTED_DIALING_USED} property and - * {@link Call.Details#PROPERTY_ASSISTED_DIALING_USED} property to be set on the + * {@link Connection#PROPERTY_ASSISTED_DIALING} property and + * {@link Call.Details#PROPERTY_ASSISTED_DIALING} property to be set on the * {@link Connection}/{@link Call} in question. When the call is logged to the call log, the * {@link android.provider.CallLog.Calls#FEATURES_ASSISTED_DIALING_USED} call feature is set to * indicate that assisted dialing was used for the call. @@ -1368,7 +1368,7 @@ public class TelecomManager { /** * Used to determine the currently selected default dialer package for a specific user. * - * @param userId the user id to query the default dialer package for. + * @param userHandle the user id to query the default dialer package for. * @return package name for the default dialer package or null if no package has been * selected as the default dialer. * @hide @@ -1376,10 +1376,11 @@ public class TelecomManager { @SystemApi @TestApi @RequiresPermission(READ_PRIVILEGED_PHONE_STATE) - public @Nullable String getDefaultDialerPackage(int userId) { + public @Nullable String getDefaultDialerPackage(@NonNull UserHandle userHandle) { try { if (isServiceConnected()) { - return getTelecomService().getDefaultDialerPackageForUser(userId); + return getTelecomService().getDefaultDialerPackageForUser( + userHandle.getIdentifier()); } } catch (RemoteException e) { Log.e(TAG, "RemoteException attempting to get the default dialer package name.", e); diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl index 4249dff151c7..a397d77db2f6 100644 --- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl +++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl @@ -104,6 +104,9 @@ oneway interface IConnectionService { void swapConference(String conferenceCallId, in Session.Info sessionInfo); + void addConferenceParticipants(String CallId, in List<Uri> participants, + in Session.Info sessionInfo); + void onPostDialContinue(String callId, boolean proceed, in Session.Info sessionInfo); void pullExternalCall(String callId, in Session.Info sessionInfo); diff --git a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl index eb2d714fe3f4..9beff22ce52e 100644 --- a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl +++ b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl @@ -67,6 +67,8 @@ oneway interface IInCallAdapter { void swapConference(String callId); + void addConferenceParticipants(String callId, in List<Uri> participants); + void turnOnProximitySensor(); void turnOffProximitySensor(boolean screenOnImmediately); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 382313304611..05a29e9524e5 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -1089,6 +1089,14 @@ public class CarrierConfigManager { "support_adhoc_conference_calls_bool"; /** + * Determines whether conference participants can be added to existing call. When {@code true}, + * adding conference participants to existing call is supported, {@code false otherwise}. + * @hide + */ + public static final String KEY_SUPPORT_ADD_CONFERENCE_PARTICIPANTS_BOOL = + "support_add_conference_participants_bool"; + + /** * Determines whether conference calls are supported by a carrier. When {@code true}, * conference calling is supported, {@code false otherwise}. */ @@ -3571,6 +3579,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_IGNORE_RTT_MODE_SETTING_BOOL, false); sDefaults.putInt(KEY_CDMA_3WAYCALL_FLASH_DELAY_INT , 0); sDefaults.putBoolean(KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL, false); + sDefaults.putBoolean(KEY_SUPPORT_ADD_CONFERENCE_PARTICIPANTS_BOOL, false); sDefaults.putBoolean(KEY_SUPPORT_CONFERENCE_CALL_BOOL, true); sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_CALL_BOOL, true); sDefaults.putBoolean(KEY_SUPPORT_MANAGE_IMS_CONFERENCE_CALL_BOOL, true); diff --git a/telephony/java/android/telephony/PreciseDisconnectCause.java b/telephony/java/android/telephony/PreciseDisconnectCause.java index 54980a29c0a6..250d9e8b212e 100644 --- a/telephony/java/android/telephony/PreciseDisconnectCause.java +++ b/telephony/java/android/telephony/PreciseDisconnectCause.java @@ -256,337 +256,6 @@ public final class PreciseDisconnectCause { /** Access Blocked by CDMA network. */ public static final int CDMA_ACCESS_BLOCKED = 1009; - /** Mapped from ImsReasonInfo */ - // TODO: remove ImsReasonInfo from preciseDisconnectCause - /* The passed argument is an invalid */ - /** @hide */ - public static final int LOCAL_ILLEGAL_ARGUMENT = 1200; - // The operation is invoked in invalid call state - /** @hide */ - public static final int LOCAL_ILLEGAL_STATE = 1201; - // IMS service internal error - /** @hide */ - public static final int LOCAL_INTERNAL_ERROR = 1202; - // IMS service goes down (service connection is lost) - /** @hide */ - public static final int LOCAL_IMS_SERVICE_DOWN = 1203; - // No pending incoming call exists - /** @hide */ - public static final int LOCAL_NO_PENDING_CALL = 1204; - // Service unavailable; by power off - /** @hide */ - public static final int LOCAL_POWER_OFF = 1205; - // Service unavailable; by low battery - /** @hide */ - public static final int LOCAL_LOW_BATTERY = 1206; - // Service unavailable; by out of service (data service state) - /** @hide */ - public static final int LOCAL_NETWORK_NO_SERVICE = 1207; - /* Service unavailable; by no LTE coverage - * (VoLTE is not supported even though IMS is registered) - */ - /** @hide */ - public static final int LOCAL_NETWORK_NO_LTE_COVERAGE = 1208; - /** Service unavailable; by located in roaming area */ - /** @hide */ - public static final int LOCAL_NETWORK_ROAMING = 1209; - /** Service unavailable; by IP changed */ - /** @hide */ - public static final int LOCAL_NETWORK_IP_CHANGED = 1210; - /** Service unavailable; other */ - /** @hide */ - public static final int LOCAL_SERVICE_UNAVAILABLE = 1211; - /* Service unavailable; IMS connection is lost (IMS is not registered) */ - /** @hide */ - public static final int LOCAL_NOT_REGISTERED = 1212; - /** Max call exceeded */ - /** @hide */ - public static final int LOCAL_MAX_CALL_EXCEEDED = 1213; - /** Call decline */ - /** @hide */ - public static final int LOCAL_CALL_DECLINE = 1214; - /** SRVCC is in progress */ - /** @hide */ - public static final int LOCAL_CALL_VCC_ON_PROGRESSING = 1215; - /** Resource reservation is failed (QoS precondition) */ - /** @hide */ - public static final int LOCAL_CALL_RESOURCE_RESERVATION_FAILED = 1216; - /** Retry CS call; VoLTE service can't be provided by the network or remote end - * Resolve the extra code(EXTRA_CODE_CALL_RETRY_*) if the below code is set - * @hide - */ - public static final int LOCAL_CALL_CS_RETRY_REQUIRED = 1217; - /** Retry VoLTE call; VoLTE service can't be provided by the network temporarily */ - /** @hide */ - public static final int LOCAL_CALL_VOLTE_RETRY_REQUIRED = 1218; - /** IMS call is already terminated (in TERMINATED state) */ - /** @hide */ - public static final int LOCAL_CALL_TERMINATED = 1219; - /** Handover not feasible */ - /** @hide */ - public static final int LOCAL_HO_NOT_FEASIBLE = 1220; - - /** 1xx waiting timer is expired after sending INVITE request (MO only) */ - /** @hide */ - public static final int TIMEOUT_1XX_WAITING = 1221; - /** User no answer during call setup operation (MO/MT) - * MO : 200 OK to INVITE request is not received, - * MT : No action from user after alerting the call - * @hide - */ - public static final int TIMEOUT_NO_ANSWER = 1222; - /** User no answer during call update operation (MO/MT) - * MO : 200 OK to re-INVITE request is not received, - * MT : No action from user after alerting the call - * @hide - */ - public static final int TIMEOUT_NO_ANSWER_CALL_UPDATE = 1223; - - /** - * STATUSCODE (SIP response code) (IMS -> Telephony) - */ - /** SIP request is redirected */ - /** @hide */ - public static final int SIP_REDIRECTED = 1300; - /** 4xx responses */ - /** 400 : Bad Request */ - /** @hide */ - public static final int SIP_BAD_REQUEST = 1310; - /** 403 : Forbidden */ - /** @hide */ - public static final int SIP_FORBIDDEN = 1311; - /** 404 : Not Found */ - /** @hide */ - public static final int SIP_NOT_FOUND = 1312; - /** 415 : Unsupported Media Type - * 416 : Unsupported URI Scheme - * 420 : Bad Extension - */ - /** @hide */ - public static final int SIP_NOT_SUPPORTED = 1313; - /** 408 : Request Timeout */ - /** @hide */ - public static final int SIP_REQUEST_TIMEOUT = 1314; - /** 480 : Temporarily Unavailable */ - /** @hide */ - public static final int SIP_TEMPRARILY_UNAVAILABLE = 1315; - /** 484 : Address Incomplete */ - /** @hide */ - public static final int SIP_BAD_ADDRESS = 1316; - /** 486 : Busy Here - * 600 : Busy Everywhere - */ - /** @hide */ - public static final int SIP_BUSY = 1317; - /** 487 : Request Terminated */ - /** @hide */ - public static final int SIP_REQUEST_CANCELLED = 1318; - /** 406 : Not Acceptable - * 488 : Not Acceptable Here - * 606 : Not Acceptable - */ - /** @hide */ - public static final int SIP_NOT_ACCEPTABLE = 1319; - /** 410 : Gone - * 604 : Does Not Exist Anywhere - */ - /** @hide */ - public static final int SIP_NOT_REACHABLE = 1320; - /** Others */ - /** @hide */ - public static final int SIP_CLIENT_ERROR = 1321; - /** 481 : Transaction Does Not Exist */ - /** @hide */ - public static final int SIP_TRANSACTION_DOES_NOT_EXIST = 1322; - /** 5xx responses - * 501 : Server Internal Error - */ - /** @hide */ - public static final int SIP_SERVER_INTERNAL_ERROR = 1330; - /** 503 : Service Unavailable */ - /** @hide */ - public static final int SIP_SERVICE_UNAVAILABLE = 1331; - /** 504 : Server Time-out */ - /** @hide */ - public static final int SIP_SERVER_TIMEOUT = 1332; - /** Others */ - /** @hide */ - public static final int SIP_SERVER_ERROR = 1333; - /** 6xx responses - * 603 : Decline - */ - /** @hide */ - public static final int SIP_USER_REJECTED = 1340; - /** Others */ - /** @hide */ - public static final int SIP_GLOBAL_ERROR = 1341; - /** Emergency failure */ - /** @hide */ - public static final int EMERGENCY_TEMP_FAILURE = 1342; - /** @hide */ - public static final int EMERGENCY_PERM_FAILURE = 1343; - /** Media resource initialization failed */ - /** @hide */ - public static final int MEDIA_INIT_FAILED = 1400; - /** RTP timeout (no audio / video traffic in the session) */ - /** @hide */ - public static final int MEDIA_NO_DATA = 1401; - /** Media is not supported; so dropped the call */ - /** @hide */ - public static final int MEDIA_NOT_ACCEPTABLE = 1402; - /** Unknown media related errors */ - /** @hide */ - public static final int MEDIA_UNSPECIFIED = 1403; - /** User triggers the call end */ - /** @hide */ - public static final int USER_TERMINATED = 1500; - /** No action while an incoming call is ringing */ - /** @hide */ - public static final int USER_NOANSWER = 1501; - /** User ignores an incoming call */ - /** @hide */ - public static final int USER_IGNORE = 1502; - /** User declines an incoming call */ - /** @hide */ - public static final int USER_DECLINE = 1503; - /** Device declines/ends a call due to low battery */ - /** @hide */ - public static final int LOW_BATTERY = 1504; - /** Device declines call due to blacklisted call ID */ - /** @hide */ - public static final int BLACKLISTED_CALL_ID = 1505; - /** The call is terminated by the network or remote user */ - /** @hide */ - public static final int USER_TERMINATED_BY_REMOTE = 1510; - - /** - * UT - */ - /** @hide */ - public static final int UT_NOT_SUPPORTED = 1800; - /** @hide */ - public static final int UT_SERVICE_UNAVAILABLE = 1801; - /** @hide */ - public static final int UT_OPERATION_NOT_ALLOWED = 1802; - /** @hide */ - public static final int UT_NETWORK_ERROR = 1803; - /** @hide */ - public static final int UT_CB_PASSWORD_MISMATCH = 1804; - - /** - * ECBM - * @hide - */ - public static final int ECBM_NOT_SUPPORTED = 1900; - - /** - * Fail code used to indicate that Multi-endpoint is not supported by the Ims framework. - * @hide - */ - public static final int MULTIENDPOINT_NOT_SUPPORTED = 1901; - - /** - * CALL DROP error codes (Call could drop because of many reasons like Network not available, - * handover, failed, etc) - */ - - /** - * CALL DROP error code for the case when a device is ePDG capable and when the user is on an - * active wifi call and at the edge of coverage and there is no qualified LTE network available - * to handover the call to. We get a handover NOT_TRIGERRED message from the modem. This error - * code is received as part of the handover message. - * @hide - */ - public static final int CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE = 2000; - - /** - * MT call has ended due to a release from the network - * because the call was answered elsewhere - * @hide - */ - public static final int ANSWERED_ELSEWHERE = 2100; - - /** - * For MultiEndpoint - Call Pull request has failed - * @hide - */ - public static final int CALL_PULL_OUT_OF_SYNC = 2101; - - /** - * For MultiEndpoint - Call has been pulled from primary to secondary - * @hide - */ - public static final int CALL_PULLED = 2102; - - /** - * Supplementary services (HOLD/RESUME) failure error codes. - * Values for Supplemetary services failure - Failed, Cancelled and Re-Invite collision. - * @hide - */ - public static final int SUPP_SVC_FAILED = 2300; - /** @hide */ - public static final int SUPP_SVC_CANCELLED = 2301; - /** @hide */ - public static final int SUPP_SVC_REINVITE_COLLISION = 2302; - - /** - * DPD Procedure received no response or send failed - * @hide - */ - public static final int IWLAN_DPD_FAILURE = 2400; - - /** - * Establishment of the ePDG Tunnel Failed - * @hide - */ - public static final int EPDG_TUNNEL_ESTABLISH_FAILURE = 2500; - - /** - * Re-keying of the ePDG Tunnel Failed; may not always result in teardown - * @hide - */ - public static final int EPDG_TUNNEL_REKEY_FAILURE = 2501; - - /** - * Connection to the packet gateway is lost - * @hide - */ - public static final int EPDG_TUNNEL_LOST_CONNECTION = 2502; - - /** - * The maximum number of calls allowed has been reached. Used in a multi-endpoint scenario - * where the number of calls across all connected devices has reached the maximum. - * @hide - */ - public static final int MAXIMUM_NUMBER_OF_CALLS_REACHED = 2503; - - /** - * Similar to {@link #CODE_LOCAL_CALL_DECLINE}, except indicates that a remote device has - * declined the call. Used in a multi-endpoint scenario where a remote device declined an - * incoming call. - * @hide - */ - public static final int REMOTE_CALL_DECLINE = 2504; - - /** - * Indicates the call was disconnected due to the user reaching their data limit. - * @hide - */ - public static final int DATA_LIMIT_REACHED = 2505; - - /** - * Indicates the call was disconnected due to the user disabling cellular data. - * @hide - */ - public static final int DATA_DISABLED = 2506; - - /** - * Indicates a call was disconnected due to loss of wifi signal. - * @hide - */ - public static final int WIFI_LOST = 2507; - - /* OEM specific error codes. To be used by OEMs when they don't want to reveal error code which would be replaced by ERROR_UNSPECIFIED */ public static final int OEM_CAUSE_1 = 0xf001; diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 89e0cec566da..2c85b2759d55 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -1100,6 +1100,16 @@ public class TelephonyManager { */ public static final int CDMA_ROAMING_MODE_ANY = 2; + /** @hide */ + @IntDef(prefix = { "CDMA_ROAMING_MODE_" }, value = { + CDMA_ROAMING_MODE_RADIO_DEFAULT, + CDMA_ROAMING_MODE_HOME, + CDMA_ROAMING_MODE_AFFILIATED, + CDMA_ROAMING_MODE_ANY + }) + @Retention(RetentionPolicy.SOURCE) + public @interface CdmaRoamingMode{} + /** * An unknown carrier id. It could either be subscription unavailable or the subscription * carrier cannot be recognized. Unrecognized carriers here means @@ -9029,8 +9039,9 @@ public class TelephonyManager { * * @hide */ - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) - public int getCdmaRoamingMode() { + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public @CdmaRoamingMode int getCdmaRoamingMode() { int mode = CDMA_ROAMING_MODE_RADIO_DEFAULT; try { ITelephony telephony = getITelephony(); @@ -9057,8 +9068,9 @@ public class TelephonyManager { * * @hide */ + @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) - public boolean setCdmaRoamingMode(int mode) { + public boolean setCdmaRoamingMode(@CdmaRoamingMode int mode) { try { ITelephony telephony = getITelephony(); if (telephony != null) { @@ -9070,6 +9082,36 @@ public class TelephonyManager { return false; } + /** @hide */ + @IntDef(flag = true, prefix = { "CDMA_SUBSCRIPTION_" }, value = { + CDMA_SUBSCRIPTION_UNKNOWN, + CDMA_SUBSCRIPTION_RUIM_SIM, + CDMA_SUBSCRIPTION_NV + }) + @Retention(RetentionPolicy.SOURCE) + public @interface CdmaSubscription{} + + /** Used for CDMA subscription mode, it'll be UNKNOWN if there is no Subscription source. + * @hide + */ + @SystemApi + public static final int CDMA_SUBSCRIPTION_UNKNOWN = -1; + + /** Used for CDMA subscription mode: RUIM/SIM (default) + * @hide + */ + @SystemApi + public static final int CDMA_SUBSCRIPTION_RUIM_SIM = 0; + + /** Used for CDMA subscription mode: NV -> non-volatile memory + * @hide + */ + @SystemApi + public static final int CDMA_SUBSCRIPTION_NV = 1; + + /** @hide */ + public static final int PREFERRED_CDMA_SUBSCRIPTION = CDMA_SUBSCRIPTION_RUIM_SIM; + /** * Sets the subscription mode for CDMA phone to the given mode {@code mode}. * @@ -9077,14 +9119,15 @@ public class TelephonyManager { * * @return {@code true} if successed. * - * @see Phone#CDMA_SUBSCRIPTION_UNKNOWN - * @see Phone#CDMA_SUBSCRIPTION_RUIM_SIM - * @see Phone#CDMA_SUBSCRIPTION_NV + * @see #CDMA_SUBSCRIPTION_UNKNOWN + * @see #CDMA_SUBSCRIPTION_RUIM_SIM + * @see #CDMA_SUBSCRIPTION_NV * * @hide */ + @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) - public boolean setCdmaSubscriptionMode(int mode) { + public boolean setCdmaSubscriptionMode(@CdmaSubscription int mode) { try { ITelephony telephony = getITelephony(); if (telephony != null) { diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java index 3e4f3d818840..efea91ab91f0 100644 --- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java +++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java @@ -272,10 +272,24 @@ public class NetworkCapabilitiesTest { netCap.setOwnerUid(123); assertParcelingIsLossless(netCap); netCap.setSSID(TEST_SSID); - assertParcelSane(netCap, 13); + assertParcelSane(netCap, 15); } @Test + public void testParcelNetworkCapabilitiesWithRequestorUidAndPackageName() { + final NetworkCapabilities netCap = new NetworkCapabilities() + .addCapability(NET_CAPABILITY_INTERNET) + .setRequestorUid(9304) + .setRequestorPackageName("com.android.test") + .addCapability(NET_CAPABILITY_EIMS) + .addCapability(NET_CAPABILITY_NOT_METERED); + assertParcelingIsLossless(netCap); + netCap.setSSID(TEST_SSID); + assertParcelSane(netCap, 15); + } + + + @Test public void testOemPaid() { NetworkCapabilities nc = new NetworkCapabilities(); // By default OEM_PAID is neither in the unwanted or required lists and the network is not diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java index 7ede14428a4f..d6bf334ee56a 100644 --- a/tests/net/java/android/net/ConnectivityManagerTest.java +++ b/tests/net/java/android/net/ConnectivityManagerTest.java @@ -212,7 +212,8 @@ public class ConnectivityManagerTest { ArgumentCaptor<Messenger> captor = ArgumentCaptor.forClass(Messenger.class); // register callback - when(mService.requestNetwork(any(), captor.capture(), anyInt(), any(), anyInt())) + when(mService.requestNetwork( + any(), captor.capture(), anyInt(), any(), anyInt(), any())) .thenReturn(request); manager.requestNetwork(request, callback, handler); @@ -240,7 +241,8 @@ public class ConnectivityManagerTest { ArgumentCaptor<Messenger> captor = ArgumentCaptor.forClass(Messenger.class); // register callback - when(mService.requestNetwork(any(), captor.capture(), anyInt(), any(), anyInt())) + when(mService.requestNetwork( + any(), captor.capture(), anyInt(), any(), anyInt(), any())) .thenReturn(req1); manager.requestNetwork(req1, callback, handler); @@ -258,7 +260,8 @@ public class ConnectivityManagerTest { verify(callback, timeout(100).times(0)).onLosing(any(), anyInt()); // callback can be registered again - when(mService.requestNetwork(any(), captor.capture(), anyInt(), any(), anyInt())) + when(mService.requestNetwork( + any(), captor.capture(), anyInt(), any(), anyInt(), any())) .thenReturn(req2); manager.requestNetwork(req2, callback, handler); @@ -282,7 +285,8 @@ public class ConnectivityManagerTest { info.targetSdkVersion = VERSION_CODES.N_MR1 + 1; when(mCtx.getApplicationInfo()).thenReturn(info); - when(mService.requestNetwork(any(), any(), anyInt(), any(), anyInt())).thenReturn(request); + when(mService.requestNetwork(any(), any(), anyInt(), any(), anyInt(), any())) + .thenReturn(request); Handler handler = new Handler(Looper.getMainLooper()); manager.requestNetwork(request, callback, handler); diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 8da1a5b6553e..b2464baf929d 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -107,6 +107,7 @@ import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -305,6 +306,7 @@ public class ConnectivityServiceTest { private static final String MOBILE_IFNAME = "test_rmnet_data0"; private static final String WIFI_IFNAME = "test_wlan0"; private static final String WIFI_WOL_IFNAME = "test_wlan_wol"; + private static final String TEST_PACKAGE_NAME = "com.android.test.package"; private static final String[] EMPTY_STRING_ARRAY = new String[0]; private MockContext mServiceContext; @@ -654,7 +656,7 @@ public class ConnectivityServiceTest { if (mNmValidationRedirectUrl != null) { mNmCallbacks.showProvisioningNotification( - "test_provisioning_notif_action", "com.android.test.package"); + "test_provisioning_notif_action", TEST_PACKAGE_NAME); mNmProvNotificationRequested = true; } } @@ -2972,7 +2974,7 @@ public class ConnectivityServiceTest { networkCapabilities.addTransportType(TRANSPORT_WIFI) .setNetworkSpecifier(new MatchAllNetworkSpecifier()); mService.requestNetwork(networkCapabilities, null, 0, null, - ConnectivityManager.TYPE_WIFI); + ConnectivityManager.TYPE_WIFI, TEST_PACKAGE_NAME); }); class NonParcelableSpecifier extends NetworkSpecifier { @@ -3011,31 +3013,12 @@ public class ConnectivityServiceTest { } @Test - public void testNetworkSpecifierUidSpoofSecurityException() throws Exception { - class UidAwareNetworkSpecifier extends NetworkSpecifier implements Parcelable { - @Override - public boolean satisfiedBy(NetworkSpecifier other) { - return true; - } - - @Override - public void assertValidFromUid(int requestorUid) { - throw new SecurityException("failure"); - } - - @Override - public int describeContents() { return 0; } - @Override - public void writeToParcel(Parcel dest, int flags) {} - } - + public void testNetworkRequestUidSpoofSecurityException() throws Exception { mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(false); - - UidAwareNetworkSpecifier networkSpecifier = new UidAwareNetworkSpecifier(); - NetworkRequest networkRequest = newWifiRequestBuilder().setNetworkSpecifier( - networkSpecifier).build(); + NetworkRequest networkRequest = newWifiRequestBuilder().build(); TestNetworkCallback networkCallback = new TestNetworkCallback(); + doThrow(new SecurityException()).when(mAppOpsManager).checkPackage(anyInt(), anyString()); assertThrows(SecurityException.class, () -> { mCm.requestNetwork(networkRequest, networkCallback); }); @@ -3171,6 +3154,7 @@ public class ConnectivityServiceTest { mCellNetworkAgent); cellNetworkCallback.expectCallback(CallbackEntry.SUSPENDED, mCellNetworkAgent); cellNetworkCallback.assertNoCallback(); + assertEquals(NetworkInfo.State.SUSPENDED, mCm.getActiveNetworkInfo().getState()); // Register a garden variety default network request. TestNetworkCallback dfltNetworkCallback = new TestNetworkCallback(); @@ -3186,6 +3170,7 @@ public class ConnectivityServiceTest { mCellNetworkAgent); cellNetworkCallback.expectCallback(CallbackEntry.RESUMED, mCellNetworkAgent); cellNetworkCallback.assertNoCallback(); + assertEquals(NetworkInfo.State.CONNECTED, mCm.getActiveNetworkInfo().getState()); dfltNetworkCallback = new TestNetworkCallback(); mCm.registerDefaultNetworkCallback(dfltNetworkCallback); diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java index 155c61f3f8c7..eb78529e8715 100644 --- a/tests/net/java/com/android/server/connectivity/VpnTest.java +++ b/tests/net/java/com/android/server/connectivity/VpnTest.java @@ -148,6 +148,7 @@ public class VpnTest { @Mock private AppOpsManager mAppOps; @Mock private NotificationManager mNotificationManager; @Mock private Vpn.SystemServices mSystemServices; + @Mock private Vpn.Ikev2SessionCreator mIkev2SessionCreator; @Mock private ConnectivityManager mConnectivityManager; @Mock private KeyStore mKeyStore; private final VpnProfile mVpnProfile = new VpnProfile("key"); @@ -867,7 +868,8 @@ public class VpnTest { * Mock some methods of vpn object. */ private Vpn createVpn(@UserIdInt int userId) { - return new Vpn(Looper.myLooper(), mContext, mNetService, userId, mSystemServices); + return new Vpn(Looper.myLooper(), mContext, mNetService, + userId, mSystemServices, mIkev2SessionCreator); } private static void assertBlocked(Vpn vpn, int... uids) { diff --git a/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java index 24aa23aec7ae..20fbc9f61a17 100644 --- a/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java +++ b/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java @@ -200,12 +200,6 @@ public final class WifiNetworkAgentSpecifier extends NetworkSpecifier implements } @Override - public void assertValidFromUid(int requestorUid) { - throw new IllegalStateException("WifiNetworkAgentSpecifier should never be used " - + "for requests."); - } - - @Override public NetworkSpecifier redact() { return null; } diff --git a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java index 6c2d7ff882d3..e1d3c434acdb 100644 --- a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java +++ b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java @@ -598,12 +598,4 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc // not make much sense! return equals(other); } - - /** @hide */ - @Override - public void assertValidFromUid(int requestorUid) { - if (this.requestorUid != requestorUid) { - throw new SecurityException("mismatched UIDs"); - } - } } diff --git a/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java b/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java index 9164d04885b3..282fda8e1b14 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java @@ -144,12 +144,6 @@ public class WifiAwareAgentNetworkSpecifier extends NetworkSpecifier implements } @Override - public void assertValidFromUid(int requestorUid) { - throw new SecurityException( - "WifiAwareAgentNetworkSpecifier should not be used in network requests"); - } - - @Override public NetworkSpecifier redact() { return null; } diff --git a/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java b/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java index 0511f2411647..ad656e1476b2 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java @@ -289,14 +289,6 @@ public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements return sb.toString(); } - /** @hide */ - @Override - public void assertValidFromUid(int requestorUid) { - if (this.requestorUid != requestorUid) { - throw new SecurityException("mismatched UIDs"); - } - } - /** * A builder class for a Wi-Fi Aware network specifier to set up an Aware connection with a * peer. diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java index e6eece85cb19..38040ea08eba 100644 --- a/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java +++ b/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java @@ -71,16 +71,6 @@ public class WifiNetworkAgentSpecifierTest { } /** - * Validate that the NetworkAgentSpecifier cannot be used in a {@link NetworkRequest} by apps. - */ - @Test(expected = IllegalStateException.class) - public void testWifiNetworkAgentSpecifierNotUsedInNetworkRequest() { - WifiNetworkAgentSpecifier specifier = createDefaultNetworkAgentSpecifier(); - - specifier.assertValidFromUid(TEST_UID); - } - - /** * Validate NetworkAgentSpecifier equals with itself. * a) Create network agent specifier 1 for WPA_PSK network * b) Create network agent specifier 2 with the same params as specifier 1. diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java index c3b62854f12c..ef9c6a389db7 100644 --- a/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java +++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java @@ -162,17 +162,6 @@ public class WifiAwareAgentNetworkSpecifierTest { collector.checkThat("Match unexpected", oldNs.satisfiedBy(newNs), equalTo(false)); } - /** - * Validate that agent network specifier cannot be used as in network requests - i.e. that - * throws an exception when queried for UID validity. - */ - @Test(expected = SecurityException.class) - public void testNoUsageInRequest() { - WifiAwareAgentNetworkSpecifier dut = new WifiAwareAgentNetworkSpecifier(); - - dut.assertValidFromUid(0); - } - // utilities /** |