diff options
49 files changed, 1631 insertions, 141 deletions
diff --git a/Android.bp b/Android.bp index 720b0453ac9d..65a1a3330154 100644 --- a/Android.bp +++ b/Android.bp @@ -830,7 +830,12 @@ aidl_interface { "core/java/android/net/IIpMemoryStore.aidl", "core/java/android/net/INetworkStackConnector.aidl", "core/java/android/net/INetworkStackStatusCallback.aidl", + "core/java/android/net/IpPrefixParcelable.aidl", + "core/java/android/net/LinkAddressParcelable.aidl", + "core/java/android/net/LinkPropertiesParcelable.aidl", "core/java/android/net/PrivateDnsConfigParcel.aidl", + "core/java/android/net/ProxyInfoParcelable.aidl", + "core/java/android/net/RouteInfoParcelable.aidl", "core/java/android/net/dhcp/DhcpServingParamsParcel.aidl", "core/java/android/net/dhcp/IDhcpServer.aidl", "core/java/android/net/dhcp/IDhcpServerCallbacks.aidl", diff --git a/api/current.txt b/api/current.txt index 7c7429ffedaf..aaacd684d48e 100755 --- a/api/current.txt +++ b/api/current.txt @@ -41302,10 +41302,10 @@ package android.telecom { ctor public CallRedirectionService(); method public final void cancelCall(); method public final android.os.IBinder onBind(android.content.Intent); - method public abstract void onPlaceCall(android.net.Uri, android.telecom.PhoneAccountHandle); + method public abstract void onPlaceCall(android.net.Uri, android.telecom.PhoneAccountHandle, boolean); method public final boolean onUnbind(android.content.Intent); method public final void placeCallUnmodified(); - method public final void redirectCall(android.net.Uri, android.telecom.PhoneAccountHandle); + method public final void redirectCall(android.net.Uri, android.telecom.PhoneAccountHandle, boolean); field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.CallRedirectionService"; } diff --git a/api/system-current.txt b/api/system-current.txt index 7daab8669530..1bf6cf995eba 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -5556,9 +5556,9 @@ package android.telephony { public class SubscriptionManager { method public java.util.List<android.telephony.SubscriptionInfo> getAvailableSubscriptionInfoList(); method public void requestEmbeddedSubscriptionInfoListRefresh(); + method public void requestEmbeddedSubscriptionInfoListRefresh(int); method public void setDefaultDataSubId(int); method public void setDefaultSmsSubId(int); - method public void requestEmbeddedSubscriptionInfoListRefresh(int); field public static final android.net.Uri ADVANCED_CALLING_ENABLED_CONTENT_URI; field public static final int PROFILE_CLASS_DEFAULT = -1; // 0xffffffff field public static final int PROFILE_CLASS_OPERATIONAL = 2; // 0x2 @@ -6116,7 +6116,6 @@ package android.telephony.ims { method public void callSessionInviteParticipantsRequestDelivered(); method public void callSessionInviteParticipantsRequestFailed(android.telephony.ims.ImsReasonInfo); method public void callSessionMayHandover(int, int); - method public void callSessionRttAudioIndicatorChanged(android.telephony.ims.ImsStreamMediaProfile); method public void callSessionMergeComplete(android.telephony.ims.stub.ImsCallSessionImplBase); method public void callSessionMergeFailed(android.telephony.ims.ImsReasonInfo); method public void callSessionMergeStarted(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile); @@ -6127,6 +6126,7 @@ package android.telephony.ims { method public void callSessionResumeFailed(android.telephony.ims.ImsReasonInfo); method public void callSessionResumeReceived(android.telephony.ims.ImsCallProfile); method public void callSessionResumed(android.telephony.ims.ImsCallProfile); + method public void callSessionRttAudioIndicatorChanged(android.telephony.ims.ImsStreamMediaProfile); method public void callSessionRttMessageReceived(java.lang.String); method public void callSessionRttModifyRequestReceived(android.telephony.ims.ImsCallProfile); method public void callSessionRttModifyResponseReceived(int); diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt index e717c1e48e62..2bd813e14937 100644 --- a/config/hiddenapi-greylist.txt +++ b/config/hiddenapi-greylist.txt @@ -1568,7 +1568,6 @@ Landroid/telephony/TelephonyManager$MultiSimVariants;->values()[Landroid/telepho Landroid/test/AndroidTestCase;->getTestContext()Landroid/content/Context; Landroid/test/AndroidTestCase;->setTestContext(Landroid/content/Context;)V Landroid/test/InstrumentationTestCase;->runMethod(Ljava/lang/reflect/Method;I)V -Landroid/test/RepetitiveTest;->numIterations()I Landroid/util/Singleton;-><init>()V Landroid/util/XmlPullAttributes;-><init>(Lorg/xmlpull/v1/XmlPullParser;)V Landroid/util/XmlPullAttributes;->mParser:Lorg/xmlpull/v1/XmlPullParser; @@ -3644,11 +3643,9 @@ Lcom/android/internal/telephony/SubscriptionInfoUpdater;->logd(Ljava/lang/String Lcom/android/internal/telephony/SubscriptionInfoUpdater;->mContext:Landroid/content/Context; Lcom/android/internal/telephony/SubscriptionInfoUpdater;->mCurrentlyActiveUserId:I Lcom/android/internal/telephony/SubscriptionInfoUpdater;->mIccId:[Ljava/lang/String; -Lcom/android/internal/telephony/SubscriptionInfoUpdater;->mInsertSimState:[I Lcom/android/internal/telephony/SubscriptionInfoUpdater;->mPackageManager:Landroid/content/pm/IPackageManager; Lcom/android/internal/telephony/SubscriptionInfoUpdater;->mPhone:[Lcom/android/internal/telephony/Phone; Lcom/android/internal/telephony/SubscriptionInfoUpdater;->PROJECT_SIM_NUM:I -Lcom/android/internal/telephony/SubscriptionInfoUpdater;->updateSubscriptionInfoByIccId()V Lcom/android/internal/telephony/TelephonyCapabilities;->supportsAdn(I)Z Lcom/android/internal/telephony/TelephonyProperties;->PROPERTY_ICC_OPERATOR_NUMERIC:Ljava/lang/String; Lcom/android/internal/telephony/test/InterpreterEx;-><init>(Ljava/lang/String;)V diff --git a/core/java/android/net/IpPrefixParcelable.aidl b/core/java/android/net/IpPrefixParcelable.aidl new file mode 100644 index 000000000000..93a8d41936cc --- /dev/null +++ b/core/java/android/net/IpPrefixParcelable.aidl @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2019 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 android.net; + +parcelable IpPrefixParcelable { + String address; + int prefixLength; +}
\ No newline at end of file diff --git a/core/java/android/net/LinkAddressParcelable.aidl b/core/java/android/net/LinkAddressParcelable.aidl new file mode 100644 index 000000000000..af8e79b21f69 --- /dev/null +++ b/core/java/android/net/LinkAddressParcelable.aidl @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2019 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 android.net; + +parcelable LinkAddressParcelable { + String address; + int prefixLength; + int flags; + int scope; +}
\ No newline at end of file diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java index 617125b3f904..c2963fd605c0 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -191,6 +191,7 @@ public final class LinkProperties implements Parcelable { } setMtu(source.mMtu); mTcpBufferSizes = source.mTcpBufferSizes; + mNat64Prefix = source.mNat64Prefix; } } diff --git a/core/java/android/net/LinkPropertiesParcelable.aidl b/core/java/android/net/LinkPropertiesParcelable.aidl new file mode 100644 index 000000000000..b153dc70e1b8 --- /dev/null +++ b/core/java/android/net/LinkPropertiesParcelable.aidl @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2019 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 android.net; + +import android.net.IpPrefixParcelable; +import android.net.LinkAddressParcelable; +import android.net.ProxyInfoParcelable; +import android.net.RouteInfoParcelable; + +parcelable LinkPropertiesParcelable { + String ifaceName; + LinkAddressParcelable[] linkAddresses; + String[] dnses; + String[] pcscfs; + String[] validatedPrivateDnses; + boolean usePrivateDns; + String privateDnsServerName; + String domains; + RouteInfoParcelable[] routes; + ProxyInfoParcelable httpProxy; + int mtu; + String tcpBufferSizes; + IpPrefixParcelable nat64Prefix; + LinkPropertiesParcelable[] stackedLinks; +}
\ No newline at end of file diff --git a/core/java/android/net/ProxyInfoParcelable.aidl b/core/java/android/net/ProxyInfoParcelable.aidl new file mode 100644 index 000000000000..59fd8467b820 --- /dev/null +++ b/core/java/android/net/ProxyInfoParcelable.aidl @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2019 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 android.net; + +parcelable ProxyInfoParcelable { + String host; + int port; + String[] exclusionList; + String pacFileUrl; +} diff --git a/core/java/android/net/RouteInfoParcelable.aidl b/core/java/android/net/RouteInfoParcelable.aidl new file mode 100644 index 000000000000..15bcdcfc2000 --- /dev/null +++ b/core/java/android/net/RouteInfoParcelable.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2019 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 android.net; + +import android.net.IpPrefixParcelable; + +parcelable RouteInfoParcelable { + IpPrefixParcelable destination; + String gatewayAddr; + String ifaceName; + int type; +} diff --git a/core/java/android/net/ipmemorystore/NetworkAttributes.java b/core/java/android/net/ipmemorystore/NetworkAttributes.java index d7e5b2761e58..b932d2197f85 100644 --- a/core/java/android/net/ipmemorystore/NetworkAttributes.java +++ b/core/java/android/net/ipmemorystore/NetworkAttributes.java @@ -28,6 +28,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.StringJoiner; /** * A POD object to represent attributes of a single L2 network entry. @@ -207,4 +208,52 @@ public class NetworkAttributes { public int hashCode() { return Objects.hash(assignedV4Address, groupHint, dnsAddresses, mtu); } + + /** Pretty print */ + @Override + public String toString() { + final StringJoiner resultJoiner = new StringJoiner(" ", "{", "}"); + final ArrayList<String> nullFields = new ArrayList<>(); + + if (null != assignedV4Address) { + resultJoiner.add("assignedV4Addr :"); + resultJoiner.add(assignedV4Address.toString()); + } else { + nullFields.add("assignedV4Addr"); + } + + if (null != groupHint) { + resultJoiner.add("groupHint :"); + resultJoiner.add(groupHint); + } else { + nullFields.add("groupHint"); + } + + if (null != dnsAddresses) { + resultJoiner.add("dnsAddr : ["); + for (final InetAddress addr : dnsAddresses) { + resultJoiner.add(addr.getHostAddress()); + } + resultJoiner.add("]"); + } else { + nullFields.add("dnsAddr"); + } + + if (null != mtu) { + resultJoiner.add("mtu :"); + resultJoiner.add(mtu.toString()); + } else { + nullFields.add("mtu"); + } + + if (!nullFields.isEmpty()) { + resultJoiner.add("; Null fields : ["); + for (final String field : nullFields) { + resultJoiner.add(field); + } + resultJoiner.add("]"); + } + + return resultJoiner.toString(); + } } diff --git a/core/java/android/net/ipmemorystore/SameL3NetworkResponse.java b/core/java/android/net/ipmemorystore/SameL3NetworkResponse.java index 0cb37e9f75b4..d040dcc3d608 100644 --- a/core/java/android/net/ipmemorystore/SameL3NetworkResponse.java +++ b/core/java/android/net/ipmemorystore/SameL3NetworkResponse.java @@ -128,4 +128,19 @@ public class SameL3NetworkResponse { public int hashCode() { return Objects.hash(l2Key1, l2Key2, confidence); } + + @Override + /** Pretty print */ + public String toString() { + switch (getNetworkSameness()) { + case NETWORK_SAME: + return "\"" + l2Key1 + "\" same L3 network as \"" + l2Key2 + "\""; + case NETWORK_DIFFERENT: + return "\"" + l2Key1 + "\" different L3 network from \"" + l2Key2 + "\""; + case NETWORK_NEVER_CONNECTED: + return "\"" + l2Key1 + "\" can't be tested against \"" + l2Key2 + "\""; + default: + return "Buggy sameness value ? \"" + l2Key1 + "\", \"" + l2Key2 + "\""; + } + } } diff --git a/core/java/android/net/ipmemorystore/Status.java b/core/java/android/net/ipmemorystore/Status.java index 5b016ec55ae1..95e504224ac7 100644 --- a/core/java/android/net/ipmemorystore/Status.java +++ b/core/java/android/net/ipmemorystore/Status.java @@ -26,6 +26,8 @@ import android.annotation.NonNull; public class Status { public static final int SUCCESS = 0; + public static final int ERROR_DATABASE_CANNOT_BE_OPENED = -1; + public final int resultCode; public Status(final int resultCode) { @@ -47,4 +49,14 @@ public class Status { public boolean isSuccess() { return SUCCESS == resultCode; } + + /** Pretty print */ + @Override + public String toString() { + switch (resultCode) { + case SUCCESS: return "SUCCESS"; + case ERROR_DATABASE_CANNOT_BE_OPENED: return "DATABASE CANNOT BE OPENED"; + default: return "Unknown value ?!"; + } + } } diff --git a/core/java/android/net/ipmemorystore/Utils.java b/core/java/android/net/ipmemorystore/Utils.java new file mode 100644 index 000000000000..73d8c83acdd9 --- /dev/null +++ b/core/java/android/net/ipmemorystore/Utils.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2018 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 android.net.ipmemorystore; + +import android.annotation.NonNull; + +/** {@hide} */ +public class Utils { + /** Pretty print */ + public static String blobToString(final Blob blob) { + final StringBuilder sb = new StringBuilder("Blob : ["); + if (blob.data.length <= 24) { + appendByteArray(sb, blob.data, 0, blob.data.length); + } else { + appendByteArray(sb, blob.data, 0, 16); + sb.append("..."); + appendByteArray(sb, blob.data, blob.data.length - 8, blob.data.length); + } + sb.append("]"); + return sb.toString(); + } + + // Adds the hex representation of the array between the specified indices (inclusive, exclusive) + private static void appendByteArray(@NonNull final StringBuilder sb, @NonNull final byte[] ar, + final int from, final int to) { + for (int i = from; i < to; ++i) { + sb.append(String.format("%02X", ar[i])); + } + } +} diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java index b15a4d3170b3..cbb3909a5536 100644 --- a/core/java/android/os/BugreportManager.java +++ b/core/java/android/os/BugreportManager.java @@ -114,7 +114,6 @@ public class BugreportManager { } } - // TODO(b/111441001) Connect up with BugreportListener methods. private final class DumpstateListener extends IDumpstateListener.Stub implements DeathRecipient { private final BugreportListener mListener; @@ -130,35 +129,35 @@ public class BugreportManager { @Override public void onProgress(int progress) throws RemoteException { - // TODO(b/111441001): implement + mListener.onProgress(progress); } @Override public void onError(int errorCode) throws RemoteException { - // TODO(b/111441001): implement + mListener.onError(errorCode); } @Override public void onFinished(long durationMs, String title, String description) throws RemoteException { - // TODO(b/111441001): implement + mListener.onFinished(durationMs, title, description); } // Old methods; should go away @Override public void onProgressUpdated(int progress) throws RemoteException { - // TODO(b/111441001): implement + // TODO(b/111441001): remove from interface } @Override public void onMaxProgressUpdated(int maxProgress) throws RemoteException { - // TODO(b/111441001): implement + // TODO(b/111441001): remove from interface } @Override public void onSectionComplete(String title, int status, int size, int durationMs) throws RemoteException { - // TODO(b/111441001): implement + // TODO(b/111441001): remove from interface } } } diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl index fdd74882eb39..8ced7225b0d4 100644 --- a/core/java/android/os/INetworkManagementService.aidl +++ b/core/java/android/os/INetworkManagementService.aidl @@ -356,16 +356,6 @@ interface INetworkManagementService void removeVpnUidRanges(int netId, in UidRange[] ranges); /** - * Start the clatd (464xlat) service on the given interface. - */ - void startClatd(String interfaceName); - - /** - * Stop the clatd (464xlat) service on the given interface. - */ - void stopClatd(String interfaceName); - - /** * Start listening for mobile activity state changes. */ void registerNetworkActivityListener(INetworkActivityListener listener); diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp index 49a24a30f77e..b2d3651079e1 100644 --- a/core/jni/android_app_NativeActivity.cpp +++ b/core/jni/android_app_NativeActivity.cpp @@ -281,15 +281,18 @@ loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jstring funcName std::unique_ptr<NativeCode> code; bool needs_native_bridge = false; + char* nativeloader_error_msg = nullptr; void* handle = OpenNativeLibrary(env, sdkVersion, pathStr.c_str(), classLoader, libraryPath, &needs_native_bridge, - &g_error_msg); + &nativeloader_error_msg); if (handle == nullptr) { + g_error_msg = nativeloader_error_msg; + NativeLoaderFreeErrorMessage(nativeloader_error_msg); ALOGW("NativeActivity LoadNativeLibrary(\"%s\") failed: %s", pathStr.c_str(), g_error_msg.c_str()); diff --git a/data/etc/Android.bp b/data/etc/Android.bp index 6ce81481c1d7..035ee108c6da 100644 --- a/data/etc/Android.bp +++ b/data/etc/Android.bp @@ -39,9 +39,6 @@ prebuilt_etc { name: "privapp-permissions-platform.xml", sub_dir: "permissions", src: "privapp-permissions-platform.xml", - required: [ - "privapp_whitelist_com.android.settings.intelligence", - ], } prebuilt_etc { @@ -86,6 +83,7 @@ prebuilt_etc { prebuilt_etc { name: "privapp_whitelist_com.android.settings.intelligence", + product_specific: true, sub_dir: "permissions", src: "com.android.settings.intelligence.xml", filename_from_src: true, diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java index afb978174784..2d7471d9aca5 100644 --- a/packages/Shell/src/com/android/shell/BugreportProgressService.java +++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java @@ -1953,8 +1953,7 @@ public class BugreportProgressService extends Service { @Override public void onProgress(int progress) throws RemoteException { - // TODO(b/111441001): change max argument? - updateProgressInfo(progress, CAPPED_MAX); + updateProgressInfo(progress, 100 /* progress is already a percentage; so max = 100 */); } @Override diff --git a/packages/services/PacProcessor/Android.mk b/packages/services/PacProcessor/Android.mk index 295b3d8bc979..be9ba4351056 100644 --- a/packages/services/PacProcessor/Android.mk +++ b/packages/services/PacProcessor/Android.mk @@ -26,6 +26,6 @@ LOCAL_PACKAGE_NAME := PacProcessor LOCAL_PRIVATE_PLATFORM_APIS := true LOCAL_CERTIFICATE := platform -LOCAL_JNI_SHARED_LIBRARIES := libjni_pacprocessor libpac +LOCAL_JNI_SHARED_LIBRARIES := libjni_pacprocessor include $(BUILD_PACKAGE) diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index d0666b98c0e0..d6f3e2ba4835 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -4879,7 +4879,7 @@ public class ConnectivityService extends IConnectivityManager.Stub final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities); final NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(), new Network(reserveNetId()), new NetworkInfo(networkInfo), lp, nc, currentScore, - mContext, mTrackerHandler, new NetworkMisc(networkMisc), this); + mContext, mTrackerHandler, new NetworkMisc(networkMisc), this, mNetd, mNMS); // Make sure the network capabilities reflect what the agent info says. nai.networkCapabilities = mixInCapabilities(nai, nc); final String extraInfo = networkInfo.getExtraInfo(); diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index 00b13207de2c..600a6ae0babd 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -17,13 +17,11 @@ package com.android.server; import static android.Manifest.permission.CONNECTIVITY_INTERNAL; -import static android.Manifest.permission.DUMP; import static android.Manifest.permission.NETWORK_SETTINGS; import static android.Manifest.permission.NETWORK_STACK; import static android.Manifest.permission.SHUTDOWN; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_NONE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NONE; @@ -40,6 +38,7 @@ import static android.net.NetworkStats.TAG_ALL; import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.UID_ALL; import static android.net.TrafficStats.UID_TETHERING; + import static com.android.server.NetworkManagementService.NetdResponseCode.ClatdStatusResult; import static com.android.server.NetworkManagementService.NetdResponseCode.InterfaceGetCfgResult; import static com.android.server.NetworkManagementService.NetdResponseCode.InterfaceListResult; @@ -53,11 +52,9 @@ import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENAB import android.annotation.NonNull; import android.app.ActivityManager; -import android.content.ContentResolver; import android.content.Context; import android.net.ConnectivityManager; import android.net.INetd; -import android.net.TetherStatsParcel; import android.net.INetworkManagementEventObserver; import android.net.ITetheringStatsProvider; import android.net.InterfaceConfiguration; @@ -69,18 +66,15 @@ import android.net.NetworkPolicyManager; import android.net.NetworkStats; import android.net.NetworkUtils; import android.net.RouteInfo; +import android.net.TetherStatsParcel; import android.net.UidRange; -import android.net.UidRangeParcel; import android.net.util.NetdService; -import android.net.wifi.WifiConfiguration; -import android.net.wifi.WifiConfiguration.KeyMgmt; import android.os.BatteryStats; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.INetworkActivityListener; import android.os.INetworkManagementService; -import android.os.PersistableBundle; import android.os.PowerManager; import android.os.Process; import android.os.RemoteCallbackList; @@ -91,12 +85,7 @@ import android.os.StrictMode; import android.os.SystemClock; import android.os.SystemProperties; import android.os.Trace; -import android.provider.Settings; import android.telephony.DataConnectionRealTimeInfo; -import android.telephony.PhoneStateListener; -import android.telephony.SubscriptionManager; -import android.telephony.TelephonyManager; -import android.text.TextUtils; import android.util.Log; import android.util.Slog; import android.util.SparseBooleanArray; @@ -109,13 +98,11 @@ import com.android.internal.net.NetworkStatsFactory; import com.android.internal.util.DumpUtils; import com.android.internal.util.HexDump; import com.android.internal.util.Preconditions; -import com.android.server.NativeDaemonConnector.Command; -import com.android.server.NativeDaemonConnector.SensitiveArg; + import com.google.android.collect.Maps; import java.io.BufferedReader; import java.io.DataInputStream; -import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.IOException; @@ -123,15 +110,11 @@ import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.InetAddress; import java.net.InterfaceAddress; -import java.net.NetworkInterface; -import java.net.SocketException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.NoSuchElementException; -import java.util.StringTokenizer; import java.util.concurrent.CountDownLatch; /** @@ -2147,28 +2130,6 @@ public class NetworkManagementService extends INetworkManagementService.Stub } @Override - public void startClatd(String interfaceName) throws IllegalStateException { - mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); - - try { - mNetdService.clatdStart(interfaceName); - } catch (RemoteException | ServiceSpecificException e) { - throw new IllegalStateException(e); - } - } - - @Override - public void stopClatd(String interfaceName) throws IllegalStateException { - mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); - - try { - mNetdService.clatdStop(interfaceName); - } catch (RemoteException | ServiceSpecificException e) { - throw new IllegalStateException(e); - } - } - - @Override public void registerNetworkActivityListener(INetworkActivityListener listener) { mNetworkActivityListeners.register(listener); } diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java index 6596d27d021e..9d9b1cfdf6e2 100644 --- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java +++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java @@ -16,8 +16,9 @@ package com.android.server.connectivity; -import android.net.InterfaceConfiguration; import android.net.ConnectivityManager; +import android.net.INetd; +import android.net.InterfaceConfiguration; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.NetworkInfo; @@ -59,6 +60,7 @@ public class Nat464Xlat extends BaseNetworkObserver { NetworkInfo.State.SUSPENDED, }; + private final INetd mNetd; private final INetworkManagementService mNMService; // The network we're running on, and its type. @@ -76,7 +78,8 @@ public class Nat464Xlat extends BaseNetworkObserver { private String mIface; private State mState = State.IDLE; - public Nat464Xlat(INetworkManagementService nmService, NetworkAgentInfo nai) { + public Nat464Xlat(NetworkAgentInfo nai, INetd netd, INetworkManagementService nmService) { + mNetd = netd; mNMService = nmService; mNetwork = nai; } @@ -140,7 +143,7 @@ public class Nat464Xlat extends BaseNetworkObserver { return; } try { - mNMService.startClatd(baseIface); + mNetd.clatdStart(baseIface); } catch(RemoteException|IllegalStateException e) { Slog.e(TAG, "Error starting clatd on " + baseIface, e); } @@ -162,7 +165,7 @@ public class Nat464Xlat extends BaseNetworkObserver { */ private void enterStoppingState() { try { - mNMService.stopClatd(mBaseIface); + mNetd.clatdStop(mBaseIface); } catch(RemoteException|IllegalStateException e) { Slog.e(TAG, "Error stopping clatd on " + mBaseIface, e); } @@ -204,7 +207,7 @@ public class Nat464Xlat extends BaseNetworkObserver { Slog.e(TAG, "startClat: Can't start clat on null interface"); return; } - // TODO: should we only do this if mNMService.startClatd() succeeds? + // TODO: should we only do this if mNetd.clatdStart() succeeds? Slog.i(TAG, "Starting clatd on " + baseIface); enterStartingState(baseIface); } diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index 54c89aa04111..9ea73fbb1882 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -17,6 +17,7 @@ package com.android.server.connectivity; import android.content.Context; +import android.net.INetd; import android.net.INetworkMonitor; import android.net.LinkProperties; import android.net.Network; @@ -239,12 +240,15 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { private static final String TAG = ConnectivityService.class.getSimpleName(); private static final boolean VDBG = false; private final ConnectivityService mConnService; + private final INetd mNetd; + private final INetworkManagementService mNMS; private final Context mContext; private final Handler mHandler; public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info, LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler, - NetworkMisc misc, ConnectivityService connService) { + NetworkMisc misc, ConnectivityService connService, INetd netd, + INetworkManagementService nms) { this.messenger = messenger; asyncChannel = ac; network = net; @@ -253,6 +257,8 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { networkCapabilities = nc; currentScore = score; mConnService = connService; + mNetd = netd; + mNMS = nms; mContext = context; mHandler = handler; networkMisc = misc; @@ -587,18 +593,18 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { public void updateClat(INetworkManagementService netd) { if (Nat464Xlat.requiresClat(this)) { - maybeStartClat(netd); + maybeStartClat(); } else { maybeStopClat(); } } /** Ensure clat has started for this network. */ - public void maybeStartClat(INetworkManagementService netd) { + public void maybeStartClat() { if (clatd != null && clatd.isStarted()) { return; } - clatd = new Nat464Xlat(netd, this); + clatd = new Nat464Xlat(this, mNetd, mNMS); clatd.start(); } diff --git a/services/core/java/com/android/server/connectivity/PacManager.java b/services/core/java/com/android/server/connectivity/PacManager.java index 3ea9810f2d76..97896889f243 100644 --- a/services/core/java/com/android/server/connectivity/PacManager.java +++ b/services/core/java/com/android/server/connectivity/PacManager.java @@ -282,6 +282,7 @@ public class PacManager { private void setCurrentProxyScript(String script) { if (mProxyService == null) { Log.e(TAG, "setCurrentProxyScript: no proxy service"); + return; } try { mProxyService.setPacFile(script); diff --git a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreDatabase.java b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreDatabase.java new file mode 100644 index 000000000000..eaab6507e8d4 --- /dev/null +++ b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreDatabase.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2018 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.net.ipmemorystore; + +import android.annotation.NonNull; +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; + +/** + * Encapsulating class for using the SQLite database backing the memory store. + * + * This class groups together the contracts and the SQLite helper used to + * use the database. + * + * @hide + */ +public class IpMemoryStoreDatabase { + /** + * Contract class for the Network Attributes table. + */ + public static class NetworkAttributesContract { + public static final String TABLENAME = "NetworkAttributes"; + + public static final String COLNAME_L2KEY = "l2Key"; + public static final String COLTYPE_L2KEY = "TEXT NOT NULL"; + + public static final String COLNAME_EXPIRYDATE = "expiryDate"; + // Milliseconds since the Epoch, in true Java style + public static final String COLTYPE_EXPIRYDATE = "BIGINT"; + + public static final String COLNAME_ASSIGNEDV4ADDRESS = "assignedV4Address"; + public static final String COLTYPE_ASSIGNEDV4ADDRESS = "INTEGER"; + + // Please note that the group hint is only a *hint*, hence its name. The client can offer + // this information to nudge the grouping in the decision it thinks is right, but it can't + // decide for the memory store what is the same L3 network. + public static final String COLNAME_GROUPHINT = "groupHint"; + public static final String COLTYPE_GROUPHINT = "TEXT"; + + public static final String COLNAME_DNSADDRESSES = "dnsAddresses"; + // Stored in marshalled form as is + public static final String COLTYPE_DNSADDRESSES = "BLOB"; + + public static final String COLNAME_MTU = "mtu"; + public static final String COLTYPE_MTU = "INTEGER"; + + public static final String CREATE_TABLE = "CREATE TABLE IF NOT EXISTS " + + TABLENAME + " (" + + COLNAME_L2KEY + " " + COLTYPE_L2KEY + " PRIMARY KEY NOT NULL, " + + COLNAME_EXPIRYDATE + " " + COLTYPE_EXPIRYDATE + ", " + + COLNAME_ASSIGNEDV4ADDRESS + " " + COLTYPE_ASSIGNEDV4ADDRESS + ", " + + COLNAME_GROUPHINT + " " + COLTYPE_GROUPHINT + ", " + + COLNAME_DNSADDRESSES + " " + COLTYPE_DNSADDRESSES + ", " + + COLNAME_MTU + " " + COLTYPE_MTU + ")"; + public static final String DROP_TABLE = "DROP TABLE IF EXISTS " + TABLENAME; + } + + /** + * Contract class for the Private Data table. + */ + public static class PrivateDataContract { + public static final String TABLENAME = "PrivateData"; + + public static final String COLNAME_L2KEY = "l2Key"; + public static final String COLTYPE_L2KEY = "TEXT NOT NULL"; + + public static final String COLNAME_CLIENT = "client"; + public static final String COLTYPE_CLIENT = "TEXT NOT NULL"; + + public static final String COLNAME_DATANAME = "dataName"; + public static final String COLTYPE_DATANAME = "TEXT NOT NULL"; + + public static final String COLNAME_DATA = "data"; + public static final String COLTYPE_DATA = "BLOB NOT NULL"; + + public static final String CREATE_TABLE = "CREATE TABLE IF NOT EXISTS " + + TABLENAME + " (" + + COLNAME_L2KEY + " " + COLTYPE_L2KEY + ", " + + COLNAME_CLIENT + " " + COLTYPE_CLIENT + ", " + + COLNAME_DATANAME + " " + COLTYPE_DATANAME + ", " + + COLNAME_DATA + " " + COLTYPE_DATA + ", " + + "PRIMARY KEY (" + + COLNAME_L2KEY + ", " + + COLNAME_CLIENT + ", " + + COLNAME_DATANAME + "))"; + public static final String DROP_TABLE = "DROP TABLE IF EXISTS " + TABLENAME; + } + + // To save memory when the DB is not used, close it after 30s of inactivity. This is + // determined manually based on what feels right. + private static final long IDLE_CONNECTION_TIMEOUT_MS = 30_000; + + /** The SQLite DB helper */ + public static class DbHelper extends SQLiteOpenHelper { + // Update this whenever changing the schema. + private static final int SCHEMA_VERSION = 1; + private static final String DATABASE_FILENAME = "IpMemoryStore.db"; + + public DbHelper(@NonNull final Context context) { + super(context, DATABASE_FILENAME, null, SCHEMA_VERSION); + setIdleConnectionTimeout(IDLE_CONNECTION_TIMEOUT_MS); + } + + /** Called when the database is created */ + public void onCreate(@NonNull final SQLiteDatabase db) { + db.execSQL(NetworkAttributesContract.CREATE_TABLE); + db.execSQL(PrivateDataContract.CREATE_TABLE); + } + + /** Called when the database is upgraded */ + public void onUpgrade(@NonNull final SQLiteDatabase db, final int oldVersion, + final int newVersion) { + // No upgrade supported yet. + db.execSQL(NetworkAttributesContract.DROP_TABLE); + db.execSQL(PrivateDataContract.DROP_TABLE); + onCreate(db); + } + + /** Called when the database is downgraded */ + public void onDowngrade(@NonNull final SQLiteDatabase db, final int oldVersion, + final int newVersion) { + // Downgrades always nuke all data and recreate an empty table. + db.execSQL(NetworkAttributesContract.DROP_TABLE); + db.execSQL(PrivateDataContract.DROP_TABLE); + onCreate(db); + } + } +} diff --git a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java index c9759bf6170f..55a72190eff4 100644 --- a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java +++ b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java @@ -19,6 +19,8 @@ package com.android.server.net.ipmemorystore; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.database.SQLException; +import android.database.sqlite.SQLiteDatabase; import android.net.IIpMemoryStore; import android.net.ipmemorystore.Blob; import android.net.ipmemorystore.IOnBlobRetrievedListener; @@ -27,6 +29,10 @@ import android.net.ipmemorystore.IOnNetworkAttributesRetrieved; import android.net.ipmemorystore.IOnSameNetworkResponseListener; import android.net.ipmemorystore.IOnStatusListener; import android.net.ipmemorystore.NetworkAttributesParcelable; +import android.util.Log; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; /** * Implementation for the IP memory store. @@ -37,10 +43,75 @@ import android.net.ipmemorystore.NetworkAttributesParcelable; * @hide */ public class IpMemoryStoreService extends IIpMemoryStore.Stub { + private static final String TAG = IpMemoryStoreService.class.getSimpleName(); + private static final int MAX_CONCURRENT_THREADS = 4; + + @NonNull final Context mContext; + @Nullable + final SQLiteDatabase mDb; + @NonNull + final ExecutorService mExecutor; + /** + * Construct an IpMemoryStoreService object. + * This constructor will block on disk access to open the database. + * @param context the context to access storage with. + */ public IpMemoryStoreService(@NonNull final Context context) { + // Note that constructing the service will access the disk and block + // for some time, but it should make no difference to the clients. Because + // the interface is one-way, clients fire and forget requests, and the callback + // will get called eventually in any case, and the framework will wait for the + // service to be created to deliver subsequent requests. + // Avoiding this would mean the mDb member can't be final, which means the service would + // have to test for nullity, care for failure, and allow for a wait at every single access, + // which would make the code a lot more complex and require all methods to possibly block. mContext = context; + SQLiteDatabase db; + final IpMemoryStoreDatabase.DbHelper helper = new IpMemoryStoreDatabase.DbHelper(context); + try { + db = helper.getWritableDatabase(); + if (null == db) Log.e(TAG, "Unexpected null return of getWriteableDatabase"); + } catch (final SQLException e) { + Log.e(TAG, "Can't open the Ip Memory Store database", e); + db = null; + } catch (final Exception e) { + Log.wtf(TAG, "Impossible exception Ip Memory Store database", e); + db = null; + } + mDb = db; + // The work-stealing thread pool executor will spawn threads as needed up to + // the max only when there is no free thread available. This generally behaves + // exactly like one would expect it intuitively : + // - When work arrives, it will spawn a new thread iff there are no available threads + // - When there is no work to do it will shutdown threads after a while (the while + // being equal to 2 seconds (not configurable) when max threads are spun up and + // twice as much for every one less thread) + // - When all threads are busy the work is enqueued and waits for any worker + // to become available. + // Because the stealing pool is made for very heavily parallel execution of + // small tasks that spawn others, it creates a queue per thread that in this + // case is overhead. However, the three behaviors above make it a superior + // choice to cached or fixedThreadPoolExecutor, neither of which can actually + // enqueue a task waiting for a thread to be free. This can probably be solved + // with judicious subclassing of ThreadPoolExecutor, but that's a lot of dangerous + // complexity for little benefit in this case. + mExecutor = Executors.newWorkStealingPool(MAX_CONCURRENT_THREADS); + } + + /** + * Shutdown the memory store service, cancelling running tasks and dropping queued tasks. + * + * This is provided to give a way to clean up, and is meant to be available in case of an + * emergency shutdown. + */ + public void shutdown() { + // By contrast with ExecutorService#shutdown, ExecutorService#shutdownNow tries + // to cancel the existing tasks, and does not wait for completion. It does not + // guarantee the threads can be terminated in any given amount of time. + mExecutor.shutdownNow(); + if (mDb != null) mDb.close(); } /** @@ -61,7 +132,7 @@ public class IpMemoryStoreService extends IIpMemoryStore.Stub { public void storeNetworkAttributes(@NonNull final String l2Key, @NonNull final NetworkAttributesParcelable attributes, @Nullable final IOnStatusListener listener) { - // TODO : implement this + // TODO : implement this. } /** @@ -79,7 +150,7 @@ public class IpMemoryStoreService extends IIpMemoryStore.Stub { public void storeBlob(@NonNull final String l2Key, @NonNull final String clientId, @NonNull final String name, @NonNull final Blob data, @Nullable final IOnStatusListener listener) { - // TODO : implement this + // TODO : implement this. } /** @@ -99,7 +170,7 @@ public class IpMemoryStoreService extends IIpMemoryStore.Stub { @Override public void findL2Key(@NonNull final NetworkAttributesParcelable attributes, @NonNull final IOnL2KeyResponseListener listener) { - // TODO : implement this + // TODO : implement this. } /** @@ -114,7 +185,7 @@ public class IpMemoryStoreService extends IIpMemoryStore.Stub { @Override public void isSameNetwork(@NonNull final String l2Key1, @NonNull final String l2Key2, @NonNull final IOnSameNetworkResponseListener listener) { - // TODO : implement this + // TODO : implement this. } /** diff --git a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/RelevanceUtils.java b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/RelevanceUtils.java new file mode 100644 index 000000000000..aa454008958d --- /dev/null +++ b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/RelevanceUtils.java @@ -0,0 +1,307 @@ +/* + * Copyright (C) 2018 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.net.ipmemorystore; + +import com.android.internal.annotations.VisibleForTesting; + +/** + * A class containing the logic around the relevance value for + * IP Memory Store. + * + * @hide + */ +public class RelevanceUtils { + /** + * The relevance is a decaying value that gets lower and lower until it + * reaches 0 after some time passes. It follows an exponential decay law, + * dropping slowly at first then faster and faster, because a network is + * likely to be visited again if it was visited not long ago, and the longer + * it hasn't been visited the more likely it is that it won't be visited + * again. For example, a network visited on holiday should stay fresh for + * the duration of the holiday and persist for a while, but after the venue + * hasn't been visited for a while it should quickly be discarded. What + * should accelerate forgetting the network is extended periods without + * visits, so that occasional venues get discarded but regular visits keep + * the network relevant, even if the visits are infrequent. + * + * This function must be stable by iteration, meaning that adjusting the same value + * for different dates iteratively multiple times should give the same result. + * Formally, if f is the decay function that associates a relevance x at a date d1 + * to the value at ulterior date d3, then for any date d2 between d1 and d3 : + * f(x, d3 - d1) = f(f(x, d3 - d2), d2 - d1). Intuitively, this property simply + * means it should be the same to compute and store back the value after two months, + * or to do it once after one month, store it back, and do it again after another + * months has passed. + * The pair of the relevance and date define the entire curve, so any pair + * of values on the curve will define the same curve. Setting one of them to a + * constant, so as not to have to store it, means the other one will always suffice + * to describe the curve. For example, only storing the date for a known, constant + * value of the relevance is an efficient way of remembering this information (and + * to compare relevances together, as f is monotonically decreasing). + * + *** Choosing the function : + * Functions of the kind described above are standard exponential decay functions + * like the ones that govern atomic decay where the value at any given date can be + * computed uniformly from the value at a previous date and the time elapsed since + * that date. It is simple to picture this kind of function as one where after a + * given period of time called the half-life, the relevance value will have been + * halved. Decay of this kind is expressed in function of the previous value by + * functions like + * f(x, t) = x * F ^ (t / L) + * ...where x is the value, t is the elapsed time, L is the half-life (or more + * generally the F-th-life) and F the decay factor (typically 0.5, hence why L is + * usually called the half-life). The ^ symbol here is used for exponentiation. + * Or, starting at a given M for t = 0 : + * f(t) = M * F ^ (t / L) + * + * Because a line in the store needs to become irrelevant at some point but + * this class of functions never go to 0, a minimum cutoff has to be chosen to + * represent irrelevance. The simpler way of doing this is to simply add this + * minimum cutoff to the computation before and removing it after. + * Thus the function becomes : + * f(x, t) = ((x + K) * F ^ (t / L)) - K + * ...where K is the minimum cutoff, L the half-life, and F the factor between + * the original x and x after its half-life. Strictly speaking using the word + * "half-life" implies that F = 0.5, but the relation works for any value of F. + * + * It is easy enough to check that this function satisfies the stability + * relation that was given above for any value of F, L and K, which become + * parameters that can be defined at will. + * + * relevance + * 1.0 | + * |\ + * | \ + * | \ (this graph rendered with L = 75 days and K = 1/40) + * 0.75| ', + * | \ + * | '. + * | \. + * | \ + * 0.5 | '\ + * | ''. + * | ''. + * | ''. + * 0.25| '''.. + * | '''.. + * | ''''.... + * | '''''.......... + * 0 +-------------------------------------------------------''''''''''---- + * 0 50 100 150 200 250 300 350 400 days + * + *** Choosing the parameters + * The maximum M is an arbitrary parameter that simply scales the curve. + * The tradeoff for M is pretty simple : if the relevance is going to be an + * integer, the bigger M is the more precision there is in the relevance. + * However, values of M that are easy for humans to read are preferable to + * help debugging, and a suitably low value may be enough to ensure there + * won't be integer overflows in intermediate computations. + * A value of 1_000_000 probably is plenty for precision, while still in the + * low range of what ints can represent. + * + * F and L are parameters to be chosen arbitrarily and have an impact on how + * fast the relevance will be decaying at first, keeping in mind that + * the 400 days value and the cap stay the same. In simpler words, F and L + * define the steepness of the curve. + * To keep things simple (and familiar) F is arbitrarily chosen to be 0.5, and + * L is set to 200 days visually to achieve the desired effect. Refer to the + * illustration above to get a feel of how that feels. + * + * Moreover, the memory store works on an assumption that the relevance should + * be capped, and that an entry with capped relevance should decay in 400 days. + * This is on premises that the networks a device will need to remember the + * longest should be networks visited about once a year. + * For this reason, the relevance is at the maximum M 400 days before expiry : + * f(M, 400 days) = 0 + * From replacing this with the value of the function, K can then be derived + * from the values of M, F and L : + * (M + K) * F ^ (t / L) - K = 0 + * K = M * F ^ (400 days / L) / (1 - F ^ (400 days / L)) + * Replacing with actual values this gives : + * K = 1_000_000 * 0.5 ^ (400 / 200) / (1 - 0.5 ^ (400 / 200)) + * = 1_000_000 / 3 ≈ 333_333.3 + * This ensures the function has the desired profile, the desired value at + * cap, and the desired value at expiry. + * + *** Useful relations + * Let's define the expiry time for any given relevance x as the interval of + * time such as : + * f(x, expiry) = 0 + * which can be rewritten + * ((x + K) * F ^ (expiry / L)) = K + * ...giving an expression of the expiry in function of the relevance x as + * expiry = L * logF(K / (x + K)) + * Conversely the relevance x can be expressed in function of the expiry as + * x = K / F ^ (expiry / L) - K + * These relations are useful in utility functions. + * + *** Bumping things up + * The last issue therefore is to decide how to bump up the relevance. The + * simple approach is to simply lift up the curve a little bit by a constant + * normalized amount, delaying the time of expiry. For example increasing + * the relevance by an amount I gives : + * x2 = x1 + I + * x2 and x1 correspond to two different expiry times expiry2 and expiry1, + * and replacing x1 and x2 in the relation above with their expression in + * function of the expiry comes : + * K / F ^ (expiry2 / L) - K = K / F ^ (expiry1 / L) - K + I + * which resolves to : + * expiry2 = L * logF(K / (I + K / F ^ (expiry1 / L))) + * + * In this implementation, the bump is defined as 1/25th of the cap for + * the relevance. This means a network will be remembered for the maximum + * period of 400 days if connected 25 times in succession not accounting + * for decay. Of course decay actually happens so it will take more than 25 + * connections for any given network to actually reach the cap, but because + * decay is slow at first, it is a good estimate of how fast cap happens. + * + * Specifically, it gives the following four results : + * - A network that a device connects to once hits irrelevance about 32.7 days after + * it was first registered if never connected again. + * - A network that a device connects to once a day at a fixed hour will hit the cap + * on the 27th connection. + * - A network that a device connects to once a week at a fixed hour will hit the cap + * on the 57th connection. + * - A network that a device connects to every day for 7 straight days then never again + * expires 144 days after the last connection. + * These metrics tend to match pretty well the requirements. + */ + + // TODO : make these constants configurable at runtime. Don't forget to build it so that + // changes will wipe the database, migrate the values, or otherwise make sure the relevance + // values are still meaningful. + + // How long, in milliseconds, is a capped relevance valid for, or in other + // words how many milliseconds after its relevance was set to RELEVANCE_CAP does + // any given line expire. 400 days. + @VisibleForTesting + public static final long CAPPED_RELEVANCE_LIFETIME_MS = 400L * 24 * 60 * 60 * 1000; + + // The constant that represents a normalized 1.0 value for the relevance. In other words, + // the cap for the relevance. This is referred to as M in the explanation above. + @VisibleForTesting + public static final int CAPPED_RELEVANCE = 1_000_000; + + // The decay factor. After a half-life, the relevance will have decayed by this value. + // This is referred to as F in the explanation above. + private static final double DECAY_FACTOR = 0.5; + + // The half-life. After this time, the relevance will have decayed by a factor DECAY_FACTOR. + // This is referred to as L in the explanation above. + private static final long HALF_LIFE_MS = 200L * 24 * 60 * 60 * 1000; + + // The value of the frame change. This is referred to as K in the explanation above. + private static final double IRRELEVANCE_FLOOR = + CAPPED_RELEVANCE * powF((double) CAPPED_RELEVANCE_LIFETIME_MS / HALF_LIFE_MS) + / (1 - powF((double) CAPPED_RELEVANCE_LIFETIME_MS / HALF_LIFE_MS)); + + // How much to bump the relevance by every time a line is written to. + @VisibleForTesting + public static final int RELEVANCE_BUMP = CAPPED_RELEVANCE / 25; + + // Java doesn't include a function for the logarithm in an arbitrary base, so implement it + private static final double LOG_DECAY_FACTOR = Math.log(DECAY_FACTOR); + private static double logF(final double value) { + return Math.log(value) / LOG_DECAY_FACTOR; + } + + // Utility function to get a power of the decay factor, to simplify the code. + private static double powF(final double value) { + return Math.pow(DECAY_FACTOR, value); + } + + /** + * Compute the value of the relevance now given an expiry date. + * + * @param expiry the date at which the column in the database expires. + * @return the adjusted value of the relevance for this moment in time. + */ + public static int computeRelevanceForNow(final long expiry) { + return computeRelevanceForTargetDate(expiry, System.currentTimeMillis()); + } + + /** + * Compute the value of the relevance at a given date from an expiry date. + * + * Because relevance decays with time, a relevance in the past corresponds to + * a different relevance later. + * + * Relevance is always a positive value. 0 means not relevant at all. + * + * See the explanation at the top of this file to get the justification for this + * computation. + * + * @param expiry the date at which the column in the database expires. + * @param target the target date to adjust the relevance to. + * @return the adjusted value of the relevance for the target moment. + */ + public static int computeRelevanceForTargetDate(final long expiry, final long target) { + final long delay = expiry - target; + if (delay >= CAPPED_RELEVANCE_LIFETIME_MS) return CAPPED_RELEVANCE; + if (delay <= 0) return 0; + return (int) (IRRELEVANCE_FLOOR / powF((float) delay / HALF_LIFE_MS) - IRRELEVANCE_FLOOR); + } + + /** + * Compute the expiry duration adjusted up for a new fresh write. + * + * Every time data is written to the memory store for a given line, the + * relevance is bumped up by a certain amount, which will boost the priority + * of this line for computation of group attributes, and delay (possibly + * indefinitely, if the line is accessed regularly) forgetting the data stored + * in that line. + * As opposed to bumpExpiryDate, this function uses a duration from now to expiry. + * + * See the explanation at the top of this file for a justification of this computation. + * + * @param oldExpiryDuration the old expiry duration in milliseconds from now. + * @return the expiry duration representing a bumped up relevance value. + */ + public static long bumpExpiryDuration(final long oldExpiryDuration) { + // L * logF(K / (I + K / F ^ (expiry1 / L))), as documented above + final double divisionFactor = powF(((double) oldExpiryDuration) / HALF_LIFE_MS); + final double oldRelevance = IRRELEVANCE_FLOOR / divisionFactor; + final long newDuration = + (long) (HALF_LIFE_MS * logF(IRRELEVANCE_FLOOR / (RELEVANCE_BUMP + oldRelevance))); + return Math.min(newDuration, CAPPED_RELEVANCE_LIFETIME_MS); + } + + /** + * Compute the new expiry date adjusted up for a new fresh write. + * + * Every time data is written to the memory store for a given line, the + * relevance is bumped up by a certain amount, which will boost the priority + * of this line for computation of group attributes, and delay (possibly + * indefinitely, if the line is accessed regularly) forgetting the data stored + * in that line. + * As opposed to bumpExpiryDuration, this function takes the old timestamp and returns the + * new timestamp. + * + * {@see bumpExpiryDuration}, and keep in mind that the bump depends on when this is called, + * because the relevance decays exponentially, therefore bumping up a high relevance (for a + * date far in the future) is less potent than bumping up a low relevance (for a date in + * a close future). + * + * @param oldExpiryDate the old date of expiration. + * @return the new expiration date after the relevance bump. + */ + public static long bumpExpiryDate(final long oldExpiryDate) { + final long now = System.currentTimeMillis(); + final long newDuration = bumpExpiryDuration(oldExpiryDate - now); + return now + newDuration; + } +} diff --git a/services/net/java/android/net/shared/LinkPropertiesParcelableUtil.java b/services/net/java/android/net/shared/LinkPropertiesParcelableUtil.java new file mode 100644 index 000000000000..5b77f543c62b --- /dev/null +++ b/services/net/java/android/net/shared/LinkPropertiesParcelableUtil.java @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2019 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 android.net.shared; + +import static android.net.shared.ParcelableUtil.fromParcelableArray; +import static android.net.shared.ParcelableUtil.toParcelableArray; + +import android.annotation.Nullable; +import android.net.InetAddresses; +import android.net.IpPrefix; +import android.net.IpPrefixParcelable; +import android.net.LinkAddress; +import android.net.LinkAddressParcelable; +import android.net.LinkProperties; +import android.net.LinkPropertiesParcelable; +import android.net.ProxyInfo; +import android.net.ProxyInfoParcelable; +import android.net.RouteInfo; +import android.net.RouteInfoParcelable; +import android.net.Uri; + +import java.net.InetAddress; +import java.util.Arrays; + +/** + * Collection of utility methods to convert to and from stable AIDL parcelables for LinkProperties + * and its attributes. + * @hide + */ +public final class LinkPropertiesParcelableUtil { + + /** + * Convert a ProxyInfo to a ProxyInfoParcelable + */ + public static ProxyInfoParcelable toStableParcelable(@Nullable ProxyInfo proxyInfo) { + if (proxyInfo == null) { + return null; + } + final ProxyInfoParcelable parcel = new ProxyInfoParcelable(); + parcel.host = proxyInfo.getHost(); + parcel.port = proxyInfo.getPort(); + parcel.exclusionList = proxyInfo.getExclusionList(); + parcel.pacFileUrl = proxyInfo.getPacFileUrl().toString(); + return parcel; + } + + /** + * Convert a ProxyInfoParcelable to a ProxyInfo + */ + public static ProxyInfo fromStableParcelable(@Nullable ProxyInfoParcelable parcel) { + if (parcel == null) { + return null; + } + if (Uri.EMPTY.toString().equals(parcel.pacFileUrl)) { + return ProxyInfo.buildDirectProxy( + parcel.host, parcel.port, Arrays.asList(parcel.exclusionList)); + } else { + return ProxyInfo.buildPacProxy(Uri.parse(parcel.pacFileUrl)); + } + } + + /** + * Convert an IpPrefixParcelable to an IpPrefix + */ + public static IpPrefixParcelable toStableParcelable(@Nullable IpPrefix ipPrefix) { + if (ipPrefix == null) { + return null; + } + final IpPrefixParcelable parcel = new IpPrefixParcelable(); + parcel.address = ipPrefix.getAddress().getHostAddress(); + parcel.prefixLength = ipPrefix.getPrefixLength(); + return parcel; + } + + /** + * Convert an IpPrefix to an IpPrefixParcelable + */ + public static IpPrefix fromStableParcelable(@Nullable IpPrefixParcelable parcel) { + if (parcel == null) { + return null; + } + return new IpPrefix(InetAddresses.parseNumericAddress(parcel.address), parcel.prefixLength); + } + + /** + * Convert a RouteInfoParcelable to a RouteInfo + */ + public static RouteInfoParcelable toStableParcelable(@Nullable RouteInfo routeInfo) { + if (routeInfo == null) { + return null; + } + final RouteInfoParcelable parcel = new RouteInfoParcelable(); + parcel.destination = toStableParcelable(routeInfo.getDestination()); + parcel.gatewayAddr = routeInfo.getGateway().getHostAddress(); + parcel.ifaceName = routeInfo.getInterface(); + parcel.type = routeInfo.getType(); + return parcel; + } + + /** + * Convert a RouteInfo to a RouteInfoParcelable + */ + public static RouteInfo fromStableParcelable(@Nullable RouteInfoParcelable parcel) { + if (parcel == null) { + return null; + } + final IpPrefix destination = fromStableParcelable(parcel.destination); + return new RouteInfo( + destination, InetAddresses.parseNumericAddress(parcel.gatewayAddr), + parcel.ifaceName, parcel.type); + } + + /** + * Convert a LinkAddressParcelable to a LinkAddress + */ + public static LinkAddressParcelable toStableParcelable(@Nullable LinkAddress la) { + if (la == null) { + return null; + } + final LinkAddressParcelable parcel = new LinkAddressParcelable(); + parcel.address = la.getAddress().getHostAddress(); + parcel.prefixLength = la.getPrefixLength(); + parcel.flags = la.getFlags(); + parcel.scope = la.getScope(); + return parcel; + } + + /** + * Convert a LinkAddress to a LinkAddressParcelable + */ + public static LinkAddress fromStableParcelable(@Nullable LinkAddressParcelable parcel) { + if (parcel == null) { + return null; + } + return new LinkAddress( + InetAddresses.parseNumericAddress(parcel.address), + parcel.prefixLength, + parcel.flags, + parcel.scope); + } + + /** + * Convert a LinkProperties to a LinkPropertiesParcelable + */ + public static LinkPropertiesParcelable toStableParcelable(@Nullable LinkProperties lp) { + if (lp == null) { + return null; + } + final LinkPropertiesParcelable parcel = new LinkPropertiesParcelable(); + parcel.ifaceName = lp.getInterfaceName(); + parcel.linkAddresses = toParcelableArray( + lp.getLinkAddresses(), + LinkPropertiesParcelableUtil::toStableParcelable, + LinkAddressParcelable.class); + parcel.dnses = toParcelableArray( + lp.getDnsServers(), InetAddress::getHostAddress, String.class); + parcel.pcscfs = toParcelableArray( + lp.getPcscfServers(), InetAddress::getHostAddress, String.class); + parcel.validatedPrivateDnses = toParcelableArray( + lp.getValidatedPrivateDnsServers(), InetAddress::getHostAddress, String.class); + parcel.usePrivateDns = lp.isPrivateDnsActive(); + parcel.privateDnsServerName = lp.getPrivateDnsServerName(); + parcel.domains = lp.getDomains(); + parcel.routes = toParcelableArray( + lp.getRoutes(), LinkPropertiesParcelableUtil::toStableParcelable, + RouteInfoParcelable.class); + parcel.httpProxy = toStableParcelable(lp.getHttpProxy()); + parcel.mtu = lp.getMtu(); + parcel.tcpBufferSizes = lp.getTcpBufferSizes(); + parcel.nat64Prefix = toStableParcelable(lp.getNat64Prefix()); + parcel.stackedLinks = toParcelableArray( + lp.getStackedLinks(), LinkPropertiesParcelableUtil::toStableParcelable, + LinkPropertiesParcelable.class); + return parcel; + } + + /** + * Convert a LinkPropertiesParcelable to a LinkProperties + */ + public static LinkProperties fromStableParcelable(@Nullable LinkPropertiesParcelable parcel) { + if (parcel == null) { + return null; + } + final LinkProperties lp = new LinkProperties(); + lp.setInterfaceName(parcel.ifaceName); + lp.setLinkAddresses(fromParcelableArray(parcel.linkAddresses, + LinkPropertiesParcelableUtil::fromStableParcelable)); + lp.setDnsServers(fromParcelableArray(parcel.dnses, InetAddresses::parseNumericAddress)); + lp.setPcscfServers(fromParcelableArray(parcel.pcscfs, InetAddresses::parseNumericAddress)); + lp.setValidatedPrivateDnsServers( + fromParcelableArray(parcel.validatedPrivateDnses, + InetAddresses::parseNumericAddress)); + lp.setUsePrivateDns(parcel.usePrivateDns); + lp.setPrivateDnsServerName(parcel.privateDnsServerName); + lp.setDomains(parcel.domains); + for (RouteInfoParcelable route : parcel.routes) { + lp.addRoute(fromStableParcelable(route)); + } + lp.setHttpProxy(fromStableParcelable(parcel.httpProxy)); + lp.setMtu(parcel.mtu); + lp.setTcpBufferSizes(parcel.tcpBufferSizes); + lp.setNat64Prefix(fromStableParcelable(parcel.nat64Prefix)); + for (LinkPropertiesParcelable stackedLink : parcel.stackedLinks) { + lp.addStackedLink(fromStableParcelable(stackedLink)); + } + return lp; + } +} diff --git a/services/net/java/android/net/shared/ParcelableUtil.java b/services/net/java/android/net/shared/ParcelableUtil.java new file mode 100644 index 000000000000..a18976c9eee6 --- /dev/null +++ b/services/net/java/android/net/shared/ParcelableUtil.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2019 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 android.net.shared; + +import android.annotation.NonNull; + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; + +/** + * Utility methods to help convert to/from stable parcelables. + * @hide + */ +public final class ParcelableUtil { + // Below methods could be implemented easily with streams, but streams are frowned upon in + // frameworks code. + + /** + * Convert a list of BaseType items to an array of ParcelableType items using the specified + * converter function. + */ + public static <ParcelableType, BaseType> ParcelableType[] toParcelableArray( + @NonNull List<BaseType> base, + @NonNull Function<BaseType, ParcelableType> conv, + @NonNull Class<ParcelableType> parcelClass) { + final ParcelableType[] out = (ParcelableType[]) Array.newInstance(parcelClass, base.size()); + int i = 0; + for (BaseType b : base) { + out[i] = conv.apply(b); + i++; + } + return out; + } + + /** + * Convert an array of ParcelableType items to a list of BaseType items using the specified + * converter function. + */ + public static <ParcelableType, BaseType> ArrayList<BaseType> fromParcelableArray( + @NonNull ParcelableType[] parceled, @NonNull Function<ParcelableType, BaseType> conv) { + final ArrayList<BaseType> out = new ArrayList<>(parceled.length); + for (ParcelableType t : parceled) { + out.add(conv.apply(t)); + } + return out; + } +} diff --git a/telecomm/java/android/telecom/CallRedirectionService.java b/telecomm/java/android/telecom/CallRedirectionService.java index b906d0bf3136..329911776993 100644 --- a/telecomm/java/android/telecom/CallRedirectionService.java +++ b/telecomm/java/android/telecom/CallRedirectionService.java @@ -16,6 +16,7 @@ package android.telecom; +import android.annotation.NonNull; import android.annotation.SdkConstant; import android.app.Service; import android.content.Intent; @@ -27,8 +28,8 @@ import android.os.Message; import android.os.RemoteException; import com.android.internal.os.SomeArgs; -import com.android.internal.telecom.ICallRedirectionService; import com.android.internal.telecom.ICallRedirectionAdapter; +import com.android.internal.telecom.ICallRedirectionService; /** * This service can be implemented to interact between Telecom and its implementor @@ -62,22 +63,35 @@ public abstract class CallRedirectionService extends Service { /** * Telecom calls this method to inform the implemented {@link CallRedirectionService} of - * a new outgoing call which is being placed. + * a new outgoing call which is being placed. Telecom does not request to redirect emergency + * calls and does not request to redirect calls with gateway information. + * + * <p>Telecom will cancel the call if Telecom does not receive a response in 5 seconds from + * the implemented {@link CallRedirectionService} set by users. * - * The implemented {@link CallRedirectionService} can call {@link #placeCallUnmodified()}, - * {@link #redirectCall(Uri, PhoneAccountHandle)}, and {@link #cancelCall()} only from here. + * <p>The implemented {@link CallRedirectionService} can call {@link #placeCallUnmodified()}, + * {@link #redirectCall(Uri, PhoneAccountHandle, boolean)}, and {@link #cancelCall()} only + * from here. * - * @param handle the phone number dialed by the user - * @param targetPhoneAccount the {@link PhoneAccountHandle} on which the call will be placed. + * @param handle the phone number dialed by the user, represented in E.164 format if possible + * @param initialPhoneAccount the {@link PhoneAccountHandle} on which the call will be placed. + * @param allowInteractiveResponse a boolean to tell if the implemented + * {@link CallRedirectionService} should allow interactive + * responses with users. Will be {@code false} if, for example + * the device is in car mode and the user would not be able to + * interact with their device. */ - public abstract void onPlaceCall(Uri handle, PhoneAccountHandle targetPhoneAccount); + public abstract void onPlaceCall(@NonNull Uri handle, + @NonNull PhoneAccountHandle initialPhoneAccount, + boolean allowInteractiveResponse); /** * The implemented {@link CallRedirectionService} calls this method to response a request - * received via {@link #onPlaceCall(Uri, PhoneAccountHandle)} to inform Telecom that no changes - * are required to the outgoing call, and that the call should be placed as-is. + * received via {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)} to inform Telecom that + * no changes are required to the outgoing call, and that the call should be placed as-is. * - * This can only be called from implemented {@link #onPlaceCall(Uri, PhoneAccountHandle)}. + * <p>This can only be called from implemented + * {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}. * */ public final void placeCallUnmodified() { @@ -89,29 +103,39 @@ public abstract class CallRedirectionService extends Service { /** * The implemented {@link CallRedirectionService} calls this method to response a request - * received via {@link #onPlaceCall(Uri, PhoneAccountHandle)} to inform Telecom that changes - * are required to the phone number or/and {@link PhoneAccountHandle} for the outgoing call. + * received via {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)} to inform Telecom that + * changes are required to the phone number or/and {@link PhoneAccountHandle} for the outgoing + * call. Telecom will cancel the call if the implemented {@link CallRedirectionService} + * replies Telecom a handle for an emergency number. * - * This can only be called from implemented {@link #onPlaceCall(Uri, PhoneAccountHandle)}. + * <p>This can only be called from implemented + * {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}. * * @param handle the new phone number to dial * @param targetPhoneAccount the {@link PhoneAccountHandle} to use when placing the call. * If {@code null}, no change will be made to the * {@link PhoneAccountHandle} used to place the call. + * @param confirmFirst Telecom will ask users to confirm the redirection via a yes/no dialog + * if the confirmFirst is true, and if the redirection request of this + * response was sent with a true flag of allowInteractiveResponse via + * {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)} */ - public final void redirectCall(Uri handle, PhoneAccountHandle targetPhoneAccount) { + public final void redirectCall(@NonNull Uri handle, + @NonNull PhoneAccountHandle targetPhoneAccount, + boolean confirmFirst) { try { - mCallRedirectionAdapter.redirectCall(handle, targetPhoneAccount); + mCallRedirectionAdapter.redirectCall(handle, targetPhoneAccount, confirmFirst); } catch (RemoteException e) { } } /** * The implemented {@link CallRedirectionService} calls this method to response a request - * received via {@link #onPlaceCall(Uri, PhoneAccountHandle)} to inform Telecom that an outgoing - * call should be canceled entirely. + * received via {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)} to inform Telecom that + * an outgoing call should be canceled entirely. * - * This can only be called from implemented {@link #onPlaceCall(Uri, PhoneAccountHandle)}. + * <p>This can only be called from implemented + * {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}. * */ public final void cancelCall() { @@ -137,7 +161,8 @@ public abstract class CallRedirectionService extends Service { SomeArgs args = (SomeArgs) msg.obj; try { mCallRedirectionAdapter = (ICallRedirectionAdapter) args.arg1; - onPlaceCall((Uri) args.arg2, (PhoneAccountHandle) args.arg3); + onPlaceCall((Uri) args.arg2, (PhoneAccountHandle) args.arg3, + (boolean) args.arg4); } finally { args.recycle(); } @@ -152,15 +177,20 @@ public abstract class CallRedirectionService extends Service { * Telecom calls this method to inform the CallRedirectionService of a new outgoing call * which is about to be placed. * @param handle the phone number dialed by the user - * @param targetPhoneAccount the URI of the number the user dialed + * @param initialPhoneAccount the URI of the number the user dialed + * @param allowInteractiveResponse a boolean to tell if the implemented + * {@link CallRedirectionService} should allow interactive + * responses with users. */ @Override - public void placeCall(ICallRedirectionAdapter adapter, Uri handle, - PhoneAccountHandle targetPhoneAccount) { + public void placeCall(@NonNull ICallRedirectionAdapter adapter, @NonNull Uri handle, + @NonNull PhoneAccountHandle initialPhoneAccount, + boolean allowInteractiveResponse) { SomeArgs args = SomeArgs.obtain(); args.arg1 = adapter; args.arg2 = handle; - args.arg3 = targetPhoneAccount; + args.arg3 = initialPhoneAccount; + args.arg4 = allowInteractiveResponse; mHandler.obtainMessage(MSG_PLACE_CALL, args).sendToTarget(); } } diff --git a/telecomm/java/com/android/internal/telecom/ICallRedirectionAdapter.aidl b/telecomm/java/com/android/internal/telecom/ICallRedirectionAdapter.aidl index 46bf983f52a2..0a42a3f4f02e 100644 --- a/telecomm/java/com/android/internal/telecom/ICallRedirectionAdapter.aidl +++ b/telecomm/java/com/android/internal/telecom/ICallRedirectionAdapter.aidl @@ -31,5 +31,6 @@ oneway interface ICallRedirectionAdapter { void placeCallUnmodified(); - void redirectCall(in Uri handle, in PhoneAccountHandle targetPhoneAccount); + void redirectCall(in Uri handle, in PhoneAccountHandle targetPhoneAccount, + boolean confirmFirst); } diff --git a/telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl b/telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl index d8d360beb64d..c1bc44007b0b 100644 --- a/telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl +++ b/telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl @@ -30,5 +30,5 @@ import com.android.internal.telecom.ICallRedirectionAdapter; */ oneway interface ICallRedirectionService { void placeCall(in ICallRedirectionAdapter adapter, in Uri handle, - in PhoneAccountHandle targetPhoneAccount); + in PhoneAccountHandle initialPhoneAccount, boolean allowInteractiveResponse); } diff --git a/telephony/java/android/telephony/CellConfigLte.java b/telephony/java/android/telephony/CellConfigLte.java index 35769f04c5ae..eafbfbc5076d 100644 --- a/telephony/java/android/telephony/CellConfigLte.java +++ b/telephony/java/android/telephony/CellConfigLte.java @@ -34,6 +34,11 @@ public class CellConfigLte implements Parcelable { } /** @hide */ + public CellConfigLte(android.hardware.radio.V1_4.CellConfigLte cellConfig) { + mIsEndcAvailable = cellConfig.isEndcAvailable; + } + + /** @hide */ public CellConfigLte(boolean isEndcAvailable) { mIsEndcAvailable = isEndcAvailable; } diff --git a/telephony/java/android/telephony/CellInfo.java b/telephony/java/android/telephony/CellInfo.java index b761bd7bf70c..8ce5c54c810e 100644 --- a/telephony/java/android/telephony/CellInfo.java +++ b/telephony/java/android/telephony/CellInfo.java @@ -19,8 +19,10 @@ package android.telephony; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.UnsupportedAppUsage; +import android.hardware.radio.V1_4.CellInfo.Info; import android.os.Parcel; import android.os.Parcelable; +import android.os.SystemClock; import com.android.internal.annotations.VisibleForTesting; @@ -318,6 +320,13 @@ public abstract class CellInfo implements Parcelable { } /** @hide */ + protected CellInfo(android.hardware.radio.V1_4.CellInfo ci) { + this.mRegistered = ci.isRegistered; + this.mTimeStamp = SystemClock.elapsedRealtimeNanos(); + this.mCellConnectionStatus = ci.connectionStatus; + } + + /** @hide */ public static CellInfo create(android.hardware.radio.V1_0.CellInfo ci) { if (ci == null) return null; switch(ci.cellInfoType) { @@ -342,4 +351,17 @@ public abstract class CellInfo implements Parcelable { default: return null; } } + + /** @hide */ + public static CellInfo create(android.hardware.radio.V1_4.CellInfo ci) { + if (ci == null) return null; + switch (ci.info.getDiscriminator()) { + case Info.hidl_discriminator.gsm: return new CellInfoGsm(ci); + case Info.hidl_discriminator.cdma: return new CellInfoCdma(ci); + case Info.hidl_discriminator.lte: return new CellInfoLte(ci); + case Info.hidl_discriminator.wcdma: return new CellInfoWcdma(ci); + case Info.hidl_discriminator.tdscdma: return new CellInfoTdscdma(ci); + default: return null; + } + } } diff --git a/telephony/java/android/telephony/CellInfoCdma.java b/telephony/java/android/telephony/CellInfoCdma.java index c9f07dab6bbd..4440108879f2 100644 --- a/telephony/java/android/telephony/CellInfoCdma.java +++ b/telephony/java/android/telephony/CellInfoCdma.java @@ -67,6 +67,15 @@ public final class CellInfoCdma extends CellInfo implements Parcelable { new CellSignalStrengthCdma(cic.signalStrengthCdma, cic.signalStrengthEvdo); } + /** @hide */ + public CellInfoCdma(android.hardware.radio.V1_4.CellInfo ci) { + super(ci); + final android.hardware.radio.V1_2.CellInfoCdma cic = ci.info.cdma(); + mCellIdentityCdma = new CellIdentityCdma(cic.cellIdentityCdma); + mCellSignalStrengthCdma = + new CellSignalStrengthCdma(cic.signalStrengthCdma, cic.signalStrengthEvdo); + } + @Override public CellIdentityCdma getCellIdentity() { return mCellIdentityCdma; diff --git a/telephony/java/android/telephony/CellInfoGsm.java b/telephony/java/android/telephony/CellInfoGsm.java index ad16dfae7295..248adfcb2d90 100644 --- a/telephony/java/android/telephony/CellInfoGsm.java +++ b/telephony/java/android/telephony/CellInfoGsm.java @@ -63,6 +63,14 @@ public final class CellInfoGsm extends CellInfo implements Parcelable { mCellSignalStrengthGsm = new CellSignalStrengthGsm(cig.signalStrengthGsm); } + /** @hide */ + public CellInfoGsm(android.hardware.radio.V1_4.CellInfo ci) { + super(ci); + final android.hardware.radio.V1_2.CellInfoGsm cig = ci.info.gsm(); + mCellIdentityGsm = new CellIdentityGsm(cig.cellIdentityGsm); + mCellSignalStrengthGsm = new CellSignalStrengthGsm(cig.signalStrengthGsm); + } + @Override public CellIdentityGsm getCellIdentity() { return mCellIdentityGsm; diff --git a/telephony/java/android/telephony/CellInfoLte.java b/telephony/java/android/telephony/CellInfoLte.java index 75938317bf44..8e8ce8a1f5de 100644 --- a/telephony/java/android/telephony/CellInfoLte.java +++ b/telephony/java/android/telephony/CellInfoLte.java @@ -70,6 +70,15 @@ public final class CellInfoLte extends CellInfo implements Parcelable { mCellConfig = new CellConfigLte(); } + /** @hide */ + public CellInfoLte(android.hardware.radio.V1_4.CellInfo ci) { + super(ci); + final android.hardware.radio.V1_4.CellInfoLte cil = ci.info.lte(); + mCellIdentityLte = new CellIdentityLte(cil.base.cellIdentityLte); + mCellSignalStrengthLte = new CellSignalStrengthLte(cil.base.signalStrengthLte); + mCellConfig = new CellConfigLte(cil.cellConfig); + } + @Override public CellIdentityLte getCellIdentity() { if (DBG) log("getCellIdentity: " + mCellIdentityLte); diff --git a/telephony/java/android/telephony/CellInfoTdscdma.java b/telephony/java/android/telephony/CellInfoTdscdma.java index a8c49b7bf68e..2ab38fb77a90 100644 --- a/telephony/java/android/telephony/CellInfoTdscdma.java +++ b/telephony/java/android/telephony/CellInfoTdscdma.java @@ -64,6 +64,14 @@ public final class CellInfoTdscdma extends CellInfo implements Parcelable { mCellSignalStrengthTdscdma = new CellSignalStrengthTdscdma(cit.signalStrengthTdscdma); } + /** @hide */ + public CellInfoTdscdma(android.hardware.radio.V1_4.CellInfo ci) { + super(ci); + final android.hardware.radio.V1_2.CellInfoTdscdma cit = ci.info.tdscdma(); + mCellIdentityTdscdma = new CellIdentityTdscdma(cit.cellIdentityTdscdma); + mCellSignalStrengthTdscdma = new CellSignalStrengthTdscdma(cit.signalStrengthTdscdma); + } + @Override public CellIdentityTdscdma getCellIdentity() { return mCellIdentityTdscdma; } diff --git a/telephony/java/android/telephony/CellInfoWcdma.java b/telephony/java/android/telephony/CellInfoWcdma.java index a427e80fd65c..65e047077639 100644 --- a/telephony/java/android/telephony/CellInfoWcdma.java +++ b/telephony/java/android/telephony/CellInfoWcdma.java @@ -63,6 +63,14 @@ public final class CellInfoWcdma extends CellInfo implements Parcelable { mCellSignalStrengthWcdma = new CellSignalStrengthWcdma(ciw.signalStrengthWcdma); } + /** @hide */ + public CellInfoWcdma(android.hardware.radio.V1_4.CellInfo ci) { + super(ci); + final android.hardware.radio.V1_2.CellInfoWcdma ciw = ci.info.wcdma(); + mCellIdentityWcdma = new CellIdentityWcdma(ciw.cellIdentityWcdma); + mCellSignalStrengthWcdma = new CellSignalStrengthWcdma(ciw.signalStrengthWcdma); + } + @Override public CellIdentityWcdma getCellIdentity() { return mCellIdentityWcdma; diff --git a/test-base/api/current.txt b/test-base/api/current.txt index 7ebd6aa8a4a2..91fcca5cac25 100644 --- a/test-base/api/current.txt +++ b/test-base/api/current.txt @@ -48,6 +48,9 @@ package android.test { method public abstract void startTiming(boolean); } + public abstract deprecated class RepetitiveTest implements java.lang.annotation.Annotation { + } + public abstract deprecated class UiThreadTest implements java.lang.annotation.Annotation { } diff --git a/test-base/src/android/test/RepetitiveTest.java b/test-base/src/android/test/RepetitiveTest.java index 6a7130e68e61..13e89d2d92a6 100644 --- a/test-base/src/android/test/RepetitiveTest.java +++ b/test-base/src/android/test/RepetitiveTest.java @@ -26,8 +26,10 @@ import java.lang.annotation.Target; * When the annotation is present, the test method is executed the number of times specified by * numIterations and defaults to 1. * - * {@hide} Not needed for public API. + * @deprecated New tests should be written using the + * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>. */ +@Deprecated @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RepetitiveTest { @@ -37,4 +39,4 @@ public @interface RepetitiveTest { * @return The total number of iterations, the default is 1. */ int numIterations() default 1; -}
\ No newline at end of file +} diff --git a/tests/net/java/android/net/LinkPropertiesTest.java b/tests/net/java/android/net/LinkPropertiesTest.java index 932fee0c1956..299fbefc78e4 100644 --- a/tests/net/java/android/net/LinkPropertiesTest.java +++ b/tests/net/java/android/net/LinkPropertiesTest.java @@ -849,6 +849,18 @@ public class LinkPropertiesTest { assertEquals(new ArraySet<>(expectRemoved), (new ArraySet<>(result.removed))); } + private void assertParcelingIsLossless(LinkProperties source) { + Parcel p = Parcel.obtain(); + source.writeToParcel(p, /* flags */ 0); + p.setDataPosition(0); + final byte[] marshalled = p.marshall(); + p = Parcel.obtain(); + p.unmarshall(marshalled, 0, marshalled.length); + p.setDataPosition(0); + LinkProperties dest = LinkProperties.CREATOR.createFromParcel(p); + assertEquals(source, dest); + } + @Test public void testLinkPropertiesParcelable() throws Exception { LinkProperties source = new LinkProperties(); @@ -870,15 +882,12 @@ public class LinkPropertiesTest { source.setNat64Prefix(new IpPrefix("2001:db8:1:2:64:64::/96")); - Parcel p = Parcel.obtain(); - source.writeToParcel(p, /* flags */ 0); - p.setDataPosition(0); - final byte[] marshalled = p.marshall(); - p = Parcel.obtain(); - p.unmarshall(marshalled, 0, marshalled.length); - p.setDataPosition(0); - LinkProperties dest = LinkProperties.CREATOR.createFromParcel(p); + assertParcelingIsLossless(source); + } - assertEquals(source, dest); + @Test + public void testParcelUninitialized() throws Exception { + LinkProperties empty = new LinkProperties(); + assertParcelingIsLossless(empty); } } diff --git a/tests/net/java/android/net/shared/LinkPropertiesParcelableUtilTest.java b/tests/net/java/android/net/shared/LinkPropertiesParcelableUtilTest.java new file mode 100644 index 000000000000..4cabfc95b49d --- /dev/null +++ b/tests/net/java/android/net/shared/LinkPropertiesParcelableUtilTest.java @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2019 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 android.net.shared; + +import static android.net.shared.LinkPropertiesParcelableUtil.fromStableParcelable; +import static android.net.shared.LinkPropertiesParcelableUtil.toStableParcelable; + +import static org.junit.Assert.assertEquals; + +import android.net.InetAddresses; +import android.net.IpPrefix; +import android.net.LinkAddress; +import android.net.LinkProperties; +import android.net.ProxyInfo; +import android.net.RouteInfo; +import android.net.Uri; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.Collections; + +/** + * Tests for {@link LinkPropertiesParcelableUtil} + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class LinkPropertiesParcelableUtilTest { + private LinkProperties mLinkProperties; + + private static final String TEST_LINKPROPS_IFACE = "TEST_IFACE"; + private static final String TEST_STACKED_LINK_1_IFACE = "TEST_STACKED_IFACE_1"; + private static final String TEST_STACKED_LINK_2_IFACE = "TEST_STACKED_IFACE_2"; + + @Before + public void setUp() { + mLinkProperties = makeLinkProperties(TEST_LINKPROPS_IFACE); + mLinkProperties.addStackedLink(makeLinkProperties(TEST_STACKED_LINK_1_IFACE)); + mLinkProperties.addStackedLink(makeLinkProperties(TEST_STACKED_LINK_2_IFACE)); + } + + private static LinkProperties makeLinkProperties(String iface) { + final LinkProperties lp = new LinkProperties(); + lp.setInterfaceName(iface); + lp.setLinkAddresses(Arrays.asList( + new LinkAddress(InetAddresses.parseNumericAddress("192.168.0.42"), 16), + new LinkAddress(InetAddresses.parseNumericAddress("2001:db8::7"), 42))); + lp.setDnsServers(Arrays.asList( + InetAddresses.parseNumericAddress("2001:db8::42"), + InetAddresses.parseNumericAddress("192.168.1.1") + )); + lp.setValidatedPrivateDnsServers(Arrays.asList( + InetAddresses.parseNumericAddress("2001:db8::43"), + InetAddresses.parseNumericAddress("192.168.42.43") + )); + lp.setPcscfServers(Arrays.asList( + InetAddresses.parseNumericAddress("2001:db8::47"), + InetAddresses.parseNumericAddress("192.168.42.47") + )); + lp.setUsePrivateDns(true); + lp.setPrivateDnsServerName("test.example.com"); + lp.setDomains("test1.example.com,test2.example.com"); + lp.addRoute(new RouteInfo( + new IpPrefix(InetAddresses.parseNumericAddress("2001:db8::44"), 45), + InetAddresses.parseNumericAddress("2001:db8::45"), + iface, + RouteInfo.RTN_UNICAST + )); + lp.addRoute(new RouteInfo( + new IpPrefix(InetAddresses.parseNumericAddress("192.168.44.45"), 16), + InetAddresses.parseNumericAddress("192.168.45.1"), + iface, + RouteInfo.RTN_THROW + )); + lp.setHttpProxy(new ProxyInfo("test3.example.com", 8000, + "excl1.example.com,excl2.example.com")); + lp.setMtu(5000); + lp.setTcpBufferSizes("1,2,3,4,5,6"); + lp.setNat64Prefix(new IpPrefix(InetAddresses.parseNumericAddress("2001:db8::48"), 96)); + + // Verify that this test does not miss any new field added later. + // If any added field is not included in LinkProperties#equals, assertLinkPropertiesEquals + // must also be updated. + assertEquals(14, Arrays.stream(LinkProperties.class.getDeclaredFields()) + .filter(f -> !Modifier.isStatic(f.getModifiers())).count()); + + return lp; + } + + @Test + public void testParcelUnparcel() { + doParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcel_NullInterface() { + mLinkProperties.setInterfaceName(null); + doParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcel_NullPrivateDnsServer() { + mLinkProperties.setPrivateDnsServerName(null); + doParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcel_NullDomains() { + mLinkProperties.setDomains(null); + doParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcel_NullProxy() { + mLinkProperties.setHttpProxy(null); + doParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcel_NullTcpBufferSizes() { + mLinkProperties.setTcpBufferSizes(null); + doParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcel_EmptyLinkAddresses() { + mLinkProperties.setLinkAddresses(Collections.emptyList()); + doParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcel_EmptyDnses() { + mLinkProperties.setDnsServers(Collections.emptyList()); + doParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcel_EmptyValidatedPrivateDnses() { + mLinkProperties.setValidatedPrivateDnsServers(Collections.emptyList()); + doParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcel_EmptyRoutes() { + for (RouteInfo r : mLinkProperties.getAllRoutes()) { + mLinkProperties.removeRoute(r); + } + doParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcel_PacFileProxyInfo() { + mLinkProperties.setHttpProxy(new ProxyInfo(Uri.parse("http://pacfile.example.com"))); + doParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcel_NullNat64Prefix() { + mLinkProperties.setNat64Prefix(null); + doParcelUnparcelTest(); + } + + private void doParcelUnparcelTest() { + final LinkProperties unparceled = fromStableParcelable(toStableParcelable(mLinkProperties)); + assertLinkPropertiesEquals(mLinkProperties, unparceled); + } + + private static void assertLinkPropertiesEquals(LinkProperties expected, LinkProperties actual) { + assertEquals(expected, actual); + + // LinkProperties equals() does not include stacked links + assertEquals(expected.getStackedLinks(), actual.getStackedLinks()); + } +} diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index bf3964416e11..2a92a7dabd98 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -4683,7 +4683,7 @@ public class ConnectivityServiceTest { mCellNetworkAgent.sendLinkProperties(cellLp); mCellNetworkAgent.connect(true); networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - verify(mNetworkManagementService, times(1)).startClatd(MOBILE_IFNAME); + verify(mMockNetd, times(1)).clatdStart(MOBILE_IFNAME); Nat464Xlat clat = mService.getNat464Xlat(mCellNetworkAgent); // Clat iface up, expect stack link updated. @@ -4710,7 +4710,7 @@ public class ConnectivityServiceTest { mCellNetworkAgent.sendLinkProperties(cellLp); waitForIdle(); networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent); - verify(mNetworkManagementService, times(1)).stopClatd(MOBILE_IFNAME); + verify(mMockNetd, times(1)).clatdStop(MOBILE_IFNAME); // Clat iface removed, expect linkproperties revert to original one clat.interfaceRemoved(CLAT_PREFIX + MOBILE_IFNAME); diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java index 4c52d818269d..9578ded1a089 100644 --- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java +++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java @@ -32,11 +32,13 @@ import android.app.PendingIntent; import android.content.Context; import android.content.res.Resources; import android.net.ConnectivityManager; +import android.net.INetd; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkMisc; import android.net.NetworkStack; +import android.os.INetworkManagementService; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.text.format.DateUtils; @@ -66,6 +68,8 @@ public class LingerMonitorTest { LingerMonitor mMonitor; @Mock ConnectivityService mConnService; + @Mock INetd mNetd; + @Mock INetworkManagementService mNMS; @Mock Context mCtx; @Mock NetworkMisc mMisc; @Mock NetworkNotificationManager mNotifier; @@ -352,7 +356,7 @@ public class LingerMonitorTest { caps.addCapability(0); caps.addTransportType(transport); NetworkAgentInfo nai = new NetworkAgentInfo(null, null, new Network(netId), info, null, - caps, 50, mCtx, null, mMisc, mConnService); + caps, 50, mCtx, null, mMisc, mConnService, mNetd, mNMS); nai.everValidated = true; return nai; } diff --git a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java index bf42412d68d8..07b1d057c882 100644 --- a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java +++ b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java @@ -17,9 +17,7 @@ package com.android.server.connectivity; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.any; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -27,6 +25,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import android.net.ConnectivityManager; +import android.net.INetd; import android.net.InterfaceConfiguration; import android.net.LinkAddress; import android.net.LinkProperties; @@ -57,6 +56,7 @@ public class Nat464XlatTest { @Mock ConnectivityService mConnectivity; @Mock NetworkMisc mMisc; + @Mock INetd mNetd; @Mock INetworkManagementService mNms; @Mock InterfaceConfiguration mConfig; @Mock NetworkAgentInfo mNai; @@ -65,7 +65,7 @@ public class Nat464XlatTest { Handler mHandler; Nat464Xlat makeNat464Xlat() { - return new Nat464Xlat(mNms, mNai); + return new Nat464Xlat(mNai, mNetd, mNms); } @Before @@ -129,7 +129,7 @@ public class Nat464XlatTest { nat.start(); verify(mNms).registerObserver(eq(nat)); - verify(mNms).startClatd(eq(BASE_IFACE)); + verify(mNetd).clatdStart(eq(BASE_IFACE)); // Stacked interface up notification arrives. nat.interfaceLinkStateChanged(STACKED_IFACE, true); @@ -144,7 +144,7 @@ public class Nat464XlatTest { // ConnectivityService stops clat (Network disconnects, IPv4 addr appears, ...). nat.stop(); - verify(mNms).stopClatd(eq(BASE_IFACE)); + verify(mNetd).clatdStop(eq(BASE_IFACE)); // Stacked interface removed notification arrives. nat.interfaceRemoved(STACKED_IFACE); @@ -156,7 +156,7 @@ public class Nat464XlatTest { assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); assertIdle(nat); - verifyNoMoreInteractions(mNms, mConnectivity); + verifyNoMoreInteractions(mNetd, mNms, mConnectivity); } @Test @@ -168,7 +168,7 @@ public class Nat464XlatTest { nat.start(); verify(mNms).registerObserver(eq(nat)); - verify(mNms).startClatd(eq(BASE_IFACE)); + verify(mNetd).clatdStart(eq(BASE_IFACE)); // Stacked interface up notification arrives. nat.interfaceLinkStateChanged(STACKED_IFACE, true); @@ -185,7 +185,7 @@ public class Nat464XlatTest { mLooper.dispatchNext(); verify(mNms).unregisterObserver(eq(nat)); - verify(mNms).stopClatd(eq(BASE_IFACE)); + verify(mNetd).clatdStop(eq(BASE_IFACE)); verify(mConnectivity, times(2)).handleUpdateLinkProperties(eq(mNai), c.capture()); assertTrue(c.getValue().getStackedLinks().isEmpty()); assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); @@ -194,7 +194,7 @@ public class Nat464XlatTest { // ConnectivityService stops clat: no-op. nat.stop(); - verifyNoMoreInteractions(mNms, mConnectivity); + verifyNoMoreInteractions(mNetd, mNms, mConnectivity); } @Test @@ -205,13 +205,13 @@ public class Nat464XlatTest { nat.start(); verify(mNms).registerObserver(eq(nat)); - verify(mNms).startClatd(eq(BASE_IFACE)); + verify(mNetd).clatdStart(eq(BASE_IFACE)); // ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...) nat.stop(); verify(mNms).unregisterObserver(eq(nat)); - verify(mNms).stopClatd(eq(BASE_IFACE)); + verify(mNetd).clatdStop(eq(BASE_IFACE)); assertIdle(nat); // In-flight interface up notification arrives: no-op @@ -225,7 +225,7 @@ public class Nat464XlatTest { assertIdle(nat); - verifyNoMoreInteractions(mNms, mConnectivity); + verifyNoMoreInteractions(mNetd, mNms, mConnectivity); } @Test @@ -236,16 +236,16 @@ public class Nat464XlatTest { nat.start(); verify(mNms).registerObserver(eq(nat)); - verify(mNms).startClatd(eq(BASE_IFACE)); + verify(mNetd).clatdStart(eq(BASE_IFACE)); // ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...) nat.stop(); verify(mNms).unregisterObserver(eq(nat)); - verify(mNms).stopClatd(eq(BASE_IFACE)); + verify(mNetd).clatdStop(eq(BASE_IFACE)); assertIdle(nat); - verifyNoMoreInteractions(mNms, mConnectivity); + verifyNoMoreInteractions(mNetd, mNms, mConnectivity); } static void assertIdle(Nat464Xlat nat) { diff --git a/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java b/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java index 859a54d29321..e63c3b02d1c3 100644 --- a/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java +++ b/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java @@ -16,6 +16,9 @@ package com.android.server.net.ipmemorystore; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; + import android.content.Context; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -26,6 +29,8 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.io.File; + /** Unit tests for {@link IpMemoryStoreServiceTest}. */ @SmallTest @RunWith(AndroidJUnit4.class) @@ -36,6 +41,7 @@ public class IpMemoryStoreServiceTest { @Before public void setUp() { MockitoAnnotations.initMocks(this); + doReturn(new File("/tmp/test.db")).when(mMockContext).getDatabasePath(anyString()); } @Test diff --git a/tests/net/java/com/android/server/net/ipmemorystore/RelevanceUtilsTests.java b/tests/net/java/com/android/server/net/ipmemorystore/RelevanceUtilsTests.java new file mode 100644 index 000000000000..8d367e2fc387 --- /dev/null +++ b/tests/net/java/com/android/server/net/ipmemorystore/RelevanceUtilsTests.java @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2018 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.net.ipmemorystore; + +import static com.android.server.net.ipmemorystore.RelevanceUtils.CAPPED_RELEVANCE; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +/** Unit tests for {@link RelevanceUtils}. */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class RelevanceUtilsTests { + @Test + public void testComputeRelevanceForTargetDate() { + final long dayInMillis = 24L * 60 * 60 * 1000; + final long base = 1_000_000L; // any given point in time + // Relevance when the network expires in 1000 years must be capped + assertEquals(CAPPED_RELEVANCE, RelevanceUtils.computeRelevanceForTargetDate( + base + 1000L * dayInMillis, base)); + // Relevance when expiry is before the date must be 0 + assertEquals(0, RelevanceUtils.computeRelevanceForTargetDate(base - 1, base)); + // Make sure the relevance for a given target date is higher if the expiry is further + // in the future + assertTrue(RelevanceUtils.computeRelevanceForTargetDate(base + 100 * dayInMillis, base) + < RelevanceUtils.computeRelevanceForTargetDate(base + 150 * dayInMillis, base)); + + // Make sure the relevance falls slower as the expiry is closing in. This is to ensure + // the decay is indeed logarithmic. + final int relevanceAtExpiry = RelevanceUtils.computeRelevanceForTargetDate(base, base); + final int relevance50DaysBeforeExpiry = + RelevanceUtils.computeRelevanceForTargetDate(base + 50 * dayInMillis, base); + final int relevance100DaysBeforeExpiry = + RelevanceUtils.computeRelevanceForTargetDate(base + 100 * dayInMillis, base); + final int relevance150DaysBeforeExpiry = + RelevanceUtils.computeRelevanceForTargetDate(base + 150 * dayInMillis, base); + assertEquals(0, relevanceAtExpiry); + assertTrue(relevance50DaysBeforeExpiry - relevanceAtExpiry + < relevance100DaysBeforeExpiry - relevance50DaysBeforeExpiry); + assertTrue(relevance100DaysBeforeExpiry - relevance50DaysBeforeExpiry + < relevance150DaysBeforeExpiry - relevance100DaysBeforeExpiry); + } + + @Test + public void testIncreaseRelevance() { + long expiry = System.currentTimeMillis(); + + final long firstBump = RelevanceUtils.bumpExpiryDate(expiry); + // Though a few milliseconds might have elapsed, the first bump should push the duration + // to days in the future, so unless this test takes literal days between these two lines, + // this should always pass. + assertTrue(firstBump > expiry); + + expiry = 0; + long lastDifference = Long.MAX_VALUE; + // The relevance should be capped in at most this many steps. Otherwise, fail. + final int steps = 1000; + for (int i = 0; i < steps; ++i) { + final long newExpiry = RelevanceUtils.bumpExpiryDuration(expiry); + if (newExpiry == expiry) { + // The relevance should be capped. Make sure it is, then exit without failure. + assertEquals(newExpiry, RelevanceUtils.CAPPED_RELEVANCE_LIFETIME_MS); + return; + } + // Make sure the new expiry is further in the future than last time. + assertTrue(newExpiry > expiry); + // Also check that it was not bumped as much as the last bump, because the + // decay must be exponential. + assertTrue(newExpiry - expiry < lastDifference); + lastDifference = newExpiry - expiry; + expiry = newExpiry; + } + fail("Relevance failed to go to the maximum value after " + steps + " bumps"); + } + + @Test + public void testContinuity() { + final long expiry = System.currentTimeMillis(); + + // Relevance at expiry and after expiry should be the cap. + final int relevanceBeforeMaxLifetime = RelevanceUtils.computeRelevanceForTargetDate(expiry, + expiry - (RelevanceUtils.CAPPED_RELEVANCE_LIFETIME_MS + 1_000_000)); + assertEquals(relevanceBeforeMaxLifetime, CAPPED_RELEVANCE); + final int relevanceForMaxLifetime = RelevanceUtils.computeRelevanceForTargetDate(expiry, + expiry - RelevanceUtils.CAPPED_RELEVANCE_LIFETIME_MS); + assertEquals(relevanceForMaxLifetime, CAPPED_RELEVANCE); + + // If the max relevance is reached at the cap lifetime, one millisecond less than this + // should be very close. Strictly speaking this is a bit brittle, but it should be + // good enough for the purposes of the memory store. + final int relevanceForOneMillisecLessThanCap = RelevanceUtils.computeRelevanceForTargetDate( + expiry, expiry - RelevanceUtils.CAPPED_RELEVANCE_LIFETIME_MS + 1); + assertTrue(relevanceForOneMillisecLessThanCap <= CAPPED_RELEVANCE); + assertTrue(relevanceForOneMillisecLessThanCap >= CAPPED_RELEVANCE - 10); + + // Likewise the relevance one millisecond before expiry should be very close to 0. It's + // fine if it rounds down to 0. + final int relevanceOneMillisecBeforeExpiry = RelevanceUtils.computeRelevanceForTargetDate( + expiry, expiry - 1); + assertTrue(relevanceOneMillisecBeforeExpiry <= 10); + assertTrue(relevanceOneMillisecBeforeExpiry >= 0); + + final int relevanceAtExpiry = RelevanceUtils.computeRelevanceForTargetDate(expiry, expiry); + assertEquals(relevanceAtExpiry, 0); + final int relevanceAfterExpiry = RelevanceUtils.computeRelevanceForTargetDate(expiry, + expiry + 1_000_000); + assertEquals(relevanceAfterExpiry, 0); + } + + // testIncreaseRelevance makes sure bumping the expiry continuously always yields a + // monotonically increasing date as a side effect, but this tests that the relevance (as + // opposed to the expiry date) increases monotonically with increasing periods. + @Test + public void testMonotonicity() { + // Hopefully the relevance is granular enough to give a different value for every one + // of this number of steps. + final int steps = 40; + final long expiry = System.currentTimeMillis(); + + int lastRelevance = -1; + for (int i = 0; i < steps; ++i) { + final long date = expiry - i * (RelevanceUtils.CAPPED_RELEVANCE_LIFETIME_MS / steps); + final int relevance = RelevanceUtils.computeRelevanceForTargetDate(expiry, date); + assertTrue(relevance > lastRelevance); + lastRelevance = relevance; + } + } +} |