From 9935dbe03eb5c188d042ce984a502893c88419d5 Mon Sep 17 00:00:00 2001 From: markchien Date: Mon, 17 May 2021 20:43:41 +0800 Subject: [PATCH] Add the tethering type to TetheringEventCallback methods Before this change, tethering always report a list of tethered interfaces and the caller need to use each tethering type's interface regex to matching tethered list to manual implement the mapping of tethering type and interface. This change allow caller to get rid of tethering interface regex. Bug: 162920185 Bug: 152203943 Test: atest CtsTetheringTest on S Ignore-AOSP-First: Currently aosp would automerge to mainlne-prod, merge to sc-dev first to avoid adding new API to mainline-prod CTS-Coverage-Bug: I already add cts test(ag/14622456), but Lint still complaint because my cts is under packages/modules/Connectivity/ but it only check whether CL touching platform/cts Change-Id: I91bcccd676d109c1b974497ac29bd366a41b8899 --- Tethering/common/TetheringLib/Android.bp | 2 + .../TetheringLib/api/module-lib-current.txt | 10 +- .../TetheringLib/api/system-current.txt | 15 +- .../src/android/net/TetherStatesParcel.aidl | 10 +- .../src/android/net/TetheringInterface.aidl | 18 +++ .../src/android/net/TetheringInterface.java | 102 ++++++++++++++ .../src/android/net/TetheringManager.java | 132 +++++++++++++++--- .../networkstack/tethering/Tethering.java | 103 ++++++++------ .../networkstack/tethering/TetheringTest.java | 11 +- 9 files changed, 331 insertions(+), 72 deletions(-) create mode 100644 Tethering/common/TetheringLib/src/android/net/TetheringInterface.aidl create mode 100644 Tethering/common/TetheringLib/src/android/net/TetheringInterface.java diff --git a/Tethering/common/TetheringLib/Android.bp b/Tethering/common/TetheringLib/Android.bp index 6bff61668e..d868f39c98 100644 --- a/Tethering/common/TetheringLib/Android.bp +++ b/Tethering/common/TetheringLib/Android.bp @@ -57,6 +57,8 @@ filegroup { "src/android/net/TetheringConfigurationParcel.aidl", "src/android/net/TetheringRequestParcel.aidl", "src/android/net/TetherStatesParcel.aidl", + "src/android/net/TetheringInterface.aidl", + "src/android/net/TetheringInterface.java", ], path: "src" } diff --git a/Tethering/common/TetheringLib/api/module-lib-current.txt b/Tethering/common/TetheringLib/api/module-lib-current.txt index 6ddb122936..0566040dd7 100644 --- a/Tethering/common/TetheringLib/api/module-lib-current.txt +++ b/Tethering/common/TetheringLib/api/module-lib-current.txt @@ -28,13 +28,13 @@ package android.net { } public static interface TetheringManager.TetheringEventCallback { - method public default void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps); + method @Deprecated public default void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps); } - public static class TetheringManager.TetheringInterfaceRegexps { - method @NonNull public java.util.List getTetherableBluetoothRegexs(); - method @NonNull public java.util.List getTetherableUsbRegexs(); - method @NonNull public java.util.List getTetherableWifiRegexs(); + @Deprecated public static class TetheringManager.TetheringInterfaceRegexps { + method @Deprecated @NonNull public java.util.List getTetherableBluetoothRegexs(); + method @Deprecated @NonNull public java.util.List getTetherableUsbRegexs(); + method @Deprecated @NonNull public java.util.List getTetherableWifiRegexs(); } } diff --git a/Tethering/common/TetheringLib/api/system-current.txt b/Tethering/common/TetheringLib/api/system-current.txt index 105bab11d5..844ff6471a 100644 --- a/Tethering/common/TetheringLib/api/system-current.txt +++ b/Tethering/common/TetheringLib/api/system-current.txt @@ -19,6 +19,15 @@ package android.net { field @NonNull public static final android.os.Parcelable.Creator CREATOR; } + public final class TetheringInterface implements android.os.Parcelable { + ctor public TetheringInterface(int, @NonNull String); + method public int describeContents(); + method @NonNull public String getInterface(); + method public int getType(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + public class TetheringManager { method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback); method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener); @@ -26,7 +35,7 @@ package android.net { method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopAllTethering(); method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int); method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback); - field public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED"; + field @Deprecated public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED"; field public static final int CONNECTIVITY_SCOPE_GLOBAL = 1; // 0x1 field public static final int CONNECTIVITY_SCOPE_LOCAL = 2; // 0x2 field public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY"; @@ -74,10 +83,14 @@ package android.net { public static interface TetheringManager.TetheringEventCallback { method public default void onClientsChanged(@NonNull java.util.Collection); method public default void onError(@NonNull String, int); + method public default void onError(@NonNull android.net.TetheringInterface, int); method public default void onLocalOnlyInterfacesChanged(@NonNull java.util.List); + method public default void onLocalOnlyInterfacesChanged(@NonNull java.util.Set); method public default void onOffloadStatusChanged(int); method public default void onTetherableInterfacesChanged(@NonNull java.util.List); + method public default void onTetherableInterfacesChanged(@NonNull java.util.Set); method public default void onTetheredInterfacesChanged(@NonNull java.util.List); + method public default void onTetheredInterfacesChanged(@NonNull java.util.Set); method public default void onTetheringSupported(boolean); method public default void onUpstreamChanged(@Nullable android.net.Network); } diff --git a/Tethering/common/TetheringLib/src/android/net/TetherStatesParcel.aidl b/Tethering/common/TetheringLib/src/android/net/TetherStatesParcel.aidl index 3d842b3374..43262fb9d1 100644 --- a/Tethering/common/TetheringLib/src/android/net/TetherStatesParcel.aidl +++ b/Tethering/common/TetheringLib/src/android/net/TetherStatesParcel.aidl @@ -16,15 +16,17 @@ package android.net; +import android.net.TetheringInterface; + /** * Status details for tethering downstream interfaces. * {@hide} */ parcelable TetherStatesParcel { - String[] availableList; - String[] tetheredList; - String[] localOnlyList; - String[] erroredIfaceList; + TetheringInterface[] availableList; + TetheringInterface[] tetheredList; + TetheringInterface[] localOnlyList; + TetheringInterface[] erroredIfaceList; // List of Last error code corresponding to each errored iface in erroredIfaceList. */ // TODO: Improve this as b/143122247. int[] lastErrorList; diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringInterface.aidl b/Tethering/common/TetheringLib/src/android/net/TetheringInterface.aidl new file mode 100644 index 0000000000..715198447f --- /dev/null +++ b/Tethering/common/TetheringLib/src/android/net/TetheringInterface.aidl @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2021 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; + +@JavaOnlyStableParcelable parcelable TetheringInterface; diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringInterface.java b/Tethering/common/TetheringLib/src/android/net/TetheringInterface.java new file mode 100644 index 0000000000..84cdef1163 --- /dev/null +++ b/Tethering/common/TetheringLib/src/android/net/TetheringInterface.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2021 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.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.net.TetheringManager.TetheringType; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * The mapping of tethering interface and type. + * @hide + */ +@SystemApi +public final class TetheringInterface implements Parcelable { + private final int mType; + private final String mInterface; + + public TetheringInterface(@TetheringType int type, @NonNull String iface) { + Objects.requireNonNull(iface); + mType = type; + mInterface = iface; + } + + private TetheringInterface(@NonNull Parcel in) { + this(in.readInt(), in.readString()); + } + + /** Get tethering type. */ + public int getType() { + return mType; + } + + /** Get tethering interface. */ + @NonNull + public String getInterface() { + return mInterface; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mType); + dest.writeString(mInterface); + } + + @Override + public int hashCode() { + return Objects.hash(mType, mInterface); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (!(obj instanceof TetheringInterface)) return false; + final TetheringInterface other = (TetheringInterface) obj; + return mType == other.mType && mInterface.equals(other.mInterface); + } + + @Override + public int describeContents() { + return 0; + } + + @NonNull + public static final Creator CREATOR = new Creator() { + @NonNull + @Override + public TetheringInterface createFromParcel(@NonNull Parcel in) { + return new TetheringInterface(in); + } + + @NonNull + @Override + public TetheringInterface[] newArray(int size) { + return new TetheringInterface[size]; + } + }; + + @NonNull + @Override + public String toString() { + return "TetheringInterface {mType=" + mType + + ", mInterface=" + mInterface + "}"; + } +} diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java index bd0b1b96bf..edd141d383 100644 --- a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java +++ b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java @@ -30,6 +30,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.ResultReceiver; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Log; import com.android.internal.annotations.GuardedBy; @@ -43,6 +44,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Objects; +import java.util.Set; import java.util.concurrent.Executor; import java.util.function.Supplier; @@ -83,7 +85,10 @@ public class TetheringManager { * {@code TetheringManager.EXTRA_ERRORED_TETHER} to indicate * the current state of tethering. Each include a list of * interface names in that state (may be empty). + * + * @deprecated New client should use TetheringEventCallback instead. */ + @Deprecated public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED"; @@ -531,7 +536,7 @@ public class TetheringManager { /** * Attempt to both alter the mode of USB and Tethering of USB. * - * @deprecated New client should not use this API anymore. All clients should use + * @deprecated New clients should not use this API anymore. All clients should use * #startTethering or #stopTethering which encapsulate proper entitlement logic. If the API is * used and an entitlement check is needed, downstream USB tethering will be enabled but will * not have any upstream. @@ -978,8 +983,13 @@ public class TetheringManager { * multiple times later upon changes. * @param reg The new regular expressions. * + * @deprecated New clients should use the callbacks with {@link TetheringInterface} which + * has the mapping between tethering type and interface. InterfaceRegex is no longer needed + * to determine the mapping of tethering type and interface. + * * @hide */ + @Deprecated @SystemApi(client = MODULE_LIBRARIES) default void onTetherableInterfaceRegexpsChanged(@NonNull TetheringInterfaceRegexps reg) {} @@ -993,15 +1003,43 @@ public class TetheringManager { */ default void onTetherableInterfacesChanged(@NonNull List interfaces) {} + /** + * Called when there was a change in the list of tetherable interfaces. Tetherable + * interface means this interface is available and can be used for tethering. + * + *

This will be called immediately after the callback is registered, and may be called + * multiple times later upon changes. + * @param interfaces The set of TetheringInterface of currently tetherable interface. + */ + default void onTetherableInterfacesChanged(@NonNull Set interfaces) { + // By default, the new callback calls the old callback, so apps + // implementing the old callback just work. + onTetherableInterfacesChanged(toIfaces(interfaces)); + } + /** * Called when there was a change in the list of tethered interfaces. * *

This will be called immediately after the callback is registered, and may be called * multiple times later upon changes. - * @param interfaces The list of 0 or more String of currently tethered interface names. + * @param interfaces The lit of 0 or more String of currently tethered interface names. */ default void onTetheredInterfacesChanged(@NonNull List interfaces) {} + /** + * Called when there was a change in the list of tethered interfaces. + * + *

This will be called immediately after the callback is registered, and may be called + * multiple times later upon changes. + * @param interfaces The set of 0 or more TetheringInterface of currently tethered + * interface. + */ + default void onTetheredInterfacesChanged(@NonNull Set interfaces) { + // By default, the new callback calls the old callback, so apps + // implementing the old callback just work. + onTetheredInterfacesChanged(toIfaces(interfaces)); + } + /** * Called when there was a change in the list of local-only interfaces. * @@ -1011,6 +1049,20 @@ public class TetheringManager { */ default void onLocalOnlyInterfacesChanged(@NonNull List interfaces) {} + /** + * Called when there was a change in the list of local-only interfaces. + * + *

This will be called immediately after the callback is registered, and may be called + * multiple times later upon changes. + * @param interfaces The set of 0 or more TetheringInterface of active local-only + * interface. + */ + default void onLocalOnlyInterfacesChanged(@NonNull Set interfaces) { + // By default, the new callback calls the old callback, so apps + // implementing the old callback just work. + onLocalOnlyInterfacesChanged(toIfaces(interfaces)); + } + /** * Called when an error occurred configuring tethering. * @@ -1021,6 +1073,20 @@ public class TetheringManager { */ default void onError(@NonNull String ifName, @TetheringIfaceError int error) {} + /** + * Called when an error occurred configuring tethering. + * + *

This will be called immediately after the callback is registered if the latest status + * on the interface is an error, and may be called multiple times later upon changes. + * @param iface The interface that experienced the error. + * @param error One of {@code TetheringManager#TETHER_ERROR_*}. + */ + default void onError(@NonNull TetheringInterface iface, @TetheringIfaceError int error) { + // By default, the new callback calls the old callback, so apps + // implementing the old callback just work. + onError(iface.getInterface(), error); + } + /** * Called when the list of tethered clients changes. * @@ -1044,9 +1110,37 @@ public class TetheringManager { } /** - * Regular expressions used to identify tethering interfaces. + * Covert DownStreamInterface collection to interface String array list. Internal use only. + * * @hide */ + public static ArrayList toIfaces(Collection tetherIfaces) { + final ArrayList ifaces = new ArrayList<>(); + for (TetheringInterface tether : tetherIfaces) { + ifaces.add(tether.getInterface()); + } + + return ifaces; + } + + private static String[] toIfaces(TetheringInterface[] tetherIfaces) { + final String[] ifaces = new String[tetherIfaces.length]; + for (int i = 0; i < tetherIfaces.length; i++) { + ifaces[i] = tetherIfaces[i].getInterface(); + } + + return ifaces; + } + + + /** + * Regular expressions used to identify tethering interfaces. + * + * @deprecated Instead of using regex to determine tethering type. New client could use the + * callbacks with {@link TetheringInterface} which has the mapping of type and interface. + * @hide + */ + @Deprecated @SystemApi(client = MODULE_LIBRARIES) public static class TetheringInterfaceRegexps { private final String[] mTetherableBluetoothRegexs; @@ -1114,10 +1208,10 @@ public class TetheringManager { } final ITetheringEventCallback remoteCallback = new ITetheringEventCallback.Stub() { // Only accessed with a lock on this object - private final HashMap mErrorStates = new HashMap<>(); - private String[] mLastTetherableInterfaces = null; - private String[] mLastTetheredInterfaces = null; - private String[] mLastLocalOnlyInterfaces = null; + private final HashMap mErrorStates = new HashMap<>(); + private TetheringInterface[] mLastTetherableInterfaces = null; + private TetheringInterface[] mLastTetheredInterfaces = null; + private TetheringInterface[] mLastLocalOnlyInterfaces = null; @Override public void onUpstreamChanged(Network network) throws RemoteException { @@ -1128,14 +1222,14 @@ public class TetheringManager { private synchronized void sendErrorCallbacks(final TetherStatesParcel newStates) { for (int i = 0; i < newStates.erroredIfaceList.length; i++) { - final String iface = newStates.erroredIfaceList[i]; - final Integer lastError = mErrorStates.get(iface); + final TetheringInterface tetherIface = newStates.erroredIfaceList[i]; + final Integer lastError = mErrorStates.get(tetherIface); final int newError = newStates.lastErrorList[i]; if (newError != TETHER_ERROR_NO_ERROR && !Objects.equals(lastError, newError)) { - callback.onError(iface, newError); + callback.onError(tetherIface, newError); } - mErrorStates.put(iface, newError); + mErrorStates.put(tetherIface, newError); } } @@ -1144,7 +1238,7 @@ public class TetheringManager { if (Arrays.equals(mLastTetherableInterfaces, newStates.availableList)) return; mLastTetherableInterfaces = newStates.availableList.clone(); callback.onTetherableInterfacesChanged( - Collections.unmodifiableList(Arrays.asList(mLastTetherableInterfaces))); + Collections.unmodifiableSet((new ArraySet(mLastTetherableInterfaces)))); } private synchronized void maybeSendTetheredIfacesChangedCallback( @@ -1152,7 +1246,7 @@ public class TetheringManager { if (Arrays.equals(mLastTetheredInterfaces, newStates.tetheredList)) return; mLastTetheredInterfaces = newStates.tetheredList.clone(); callback.onTetheredInterfacesChanged( - Collections.unmodifiableList(Arrays.asList(mLastTetheredInterfaces))); + Collections.unmodifiableSet((new ArraySet(mLastTetheredInterfaces)))); } private synchronized void maybeSendLocalOnlyIfacesChangedCallback( @@ -1160,7 +1254,7 @@ public class TetheringManager { if (Arrays.equals(mLastLocalOnlyInterfaces, newStates.localOnlyList)) return; mLastLocalOnlyInterfaces = newStates.localOnlyList.clone(); callback.onLocalOnlyInterfacesChanged( - Collections.unmodifiableList(Arrays.asList(mLastLocalOnlyInterfaces))); + Collections.unmodifiableSet((new ArraySet(mLastLocalOnlyInterfaces)))); } // Called immediately after the callbacks are registered. @@ -1262,8 +1356,8 @@ public class TetheringManager { if (mTetherStatesParcel == null) return TETHER_ERROR_NO_ERROR; int i = 0; - for (String errored : mTetherStatesParcel.erroredIfaceList) { - if (iface.equals(errored)) return mTetherStatesParcel.lastErrorList[i]; + for (TetheringInterface errored : mTetherStatesParcel.erroredIfaceList) { + if (iface.equals(errored.getInterface())) return mTetherStatesParcel.lastErrorList[i]; i++; } @@ -1327,7 +1421,7 @@ public class TetheringManager { mCallback.waitForStarted(); if (mTetherStatesParcel == null) return new String[0]; - return mTetherStatesParcel.availableList; + return toIfaces(mTetherStatesParcel.availableList); } /** @@ -1341,7 +1435,7 @@ public class TetheringManager { mCallback.waitForStarted(); if (mTetherStatesParcel == null) return new String[0]; - return mTetherStatesParcel.tetheredList; + return toIfaces(mTetherStatesParcel.tetheredList); } /** @@ -1361,7 +1455,7 @@ public class TetheringManager { mCallback.waitForStarted(); if (mTetherStatesParcel == null) return new String[0]; - return mTetherStatesParcel.erroredIfaceList; + return toIfaces(mTetherStatesParcel.erroredIfaceList); } /** diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java index 0e8b2b5e71..759638083f 100644 --- a/Tethering/src/com/android/networkstack/tethering/Tethering.java +++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java @@ -51,6 +51,7 @@ import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_TYPE; import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_FAILED; import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STARTED; import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STOPPED; +import static android.net.TetheringManager.toIfaces; import static android.net.util.TetheringMessageBase.BASE_MAIN_SM; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE; @@ -92,6 +93,7 @@ import android.net.TetherStatesParcel; import android.net.TetheredClient; import android.net.TetheringCallbackStartedParcel; import android.net.TetheringConfigurationParcel; +import android.net.TetheringInterface; import android.net.TetheringManager.TetheringRequest; import android.net.TetheringRequestParcel; import android.net.ip.IpServer; @@ -143,7 +145,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.concurrent.CountDownLatch; @@ -854,26 +855,27 @@ public class Tethering { private void sendTetherStateChangedBroadcast() { if (!isTetheringSupported()) return; - final ArrayList availableList = new ArrayList<>(); - final ArrayList tetherList = new ArrayList<>(); - final ArrayList localOnlyList = new ArrayList<>(); - final ArrayList erroredList = new ArrayList<>(); - final ArrayList lastErrorList = new ArrayList<>(); + final ArrayList available = new ArrayList<>(); + final ArrayList tethered = new ArrayList<>(); + final ArrayList localOnly = new ArrayList<>(); + final ArrayList errored = new ArrayList<>(); + final ArrayList lastErrors = new ArrayList<>(); final TetheringConfiguration cfg = mConfig; - mTetherStatesParcel = new TetherStatesParcel(); int downstreamTypesMask = DOWNSTREAM_NONE; for (int i = 0; i < mTetherStates.size(); i++) { - TetherState tetherState = mTetherStates.valueAt(i); - String iface = mTetherStates.keyAt(i); + final TetherState tetherState = mTetherStates.valueAt(i); + final int type = tetherState.ipServer.interfaceType(); + final String iface = mTetherStates.keyAt(i); + final TetheringInterface tetheringIface = new TetheringInterface(type, iface); if (tetherState.lastError != TETHER_ERROR_NO_ERROR) { - erroredList.add(iface); - lastErrorList.add(tetherState.lastError); + errored.add(tetheringIface); + lastErrors.add(tetherState.lastError); } else if (tetherState.lastState == IpServer.STATE_AVAILABLE) { - availableList.add(iface); + available.add(tetheringIface); } else if (tetherState.lastState == IpServer.STATE_LOCAL_ONLY) { - localOnlyList.add(iface); + localOnly.add(tetheringIface); } else if (tetherState.lastState == IpServer.STATE_TETHERED) { if (cfg.isUsb(iface)) { downstreamTypesMask |= (1 << TETHERING_USB); @@ -882,40 +884,63 @@ public class Tethering { } else if (cfg.isBluetooth(iface)) { downstreamTypesMask |= (1 << TETHERING_BLUETOOTH); } - tetherList.add(iface); + tethered.add(tetheringIface); } } - mTetherStatesParcel.availableList = availableList.toArray(new String[0]); - mTetherStatesParcel.tetheredList = tetherList.toArray(new String[0]); - mTetherStatesParcel.localOnlyList = localOnlyList.toArray(new String[0]); - mTetherStatesParcel.erroredIfaceList = erroredList.toArray(new String[0]); - mTetherStatesParcel.lastErrorList = new int[lastErrorList.size()]; - Iterator iterator = lastErrorList.iterator(); - for (int i = 0; i < lastErrorList.size(); i++) { - mTetherStatesParcel.lastErrorList[i] = iterator.next().intValue(); - } + mTetherStatesParcel = buildTetherStatesParcel(available, localOnly, tethered, errored, + lastErrors); reportTetherStateChanged(mTetherStatesParcel); - final Intent bcast = new Intent(ACTION_TETHER_STATE_CHANGED); - bcast.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); - bcast.putStringArrayListExtra(EXTRA_AVAILABLE_TETHER, availableList); - bcast.putStringArrayListExtra(EXTRA_ACTIVE_LOCAL_ONLY, localOnlyList); - bcast.putStringArrayListExtra(EXTRA_ACTIVE_TETHER, tetherList); - bcast.putStringArrayListExtra(EXTRA_ERRORED_TETHER, erroredList); - mContext.sendStickyBroadcastAsUser(bcast, UserHandle.ALL); + mContext.sendStickyBroadcastAsUser(buildStateChangeIntent(available, localOnly, tethered, + errored), UserHandle.ALL); if (DBG) { Log.d(TAG, String.format( - "sendTetherStateChangedBroadcast %s=[%s] %s=[%s] %s=[%s] %s=[%s]", - "avail", TextUtils.join(",", availableList), - "local_only", TextUtils.join(",", localOnlyList), - "tether", TextUtils.join(",", tetherList), - "error", TextUtils.join(",", erroredList))); + "reportTetherStateChanged %s=[%s] %s=[%s] %s=[%s] %s=[%s]", + "avail", TextUtils.join(",", available), + "local_only", TextUtils.join(",", localOnly), + "tether", TextUtils.join(",", tethered), + "error", TextUtils.join(",", errored))); } mNotificationUpdater.onDownstreamChanged(downstreamTypesMask); } + private TetherStatesParcel buildTetherStatesParcel( + final ArrayList available, + final ArrayList localOnly, + final ArrayList tethered, + final ArrayList errored, + final ArrayList lastErrors) { + final TetherStatesParcel parcel = new TetherStatesParcel(); + + parcel.availableList = available.toArray(new TetheringInterface[0]); + parcel.tetheredList = tethered.toArray(new TetheringInterface[0]); + parcel.localOnlyList = localOnly.toArray(new TetheringInterface[0]); + parcel.erroredIfaceList = errored.toArray(new TetheringInterface[0]); + parcel.lastErrorList = new int[lastErrors.size()]; + for (int i = 0; i < lastErrors.size(); i++) { + parcel.lastErrorList[i] = lastErrors.get(i); + } + + return parcel; + } + + private Intent buildStateChangeIntent(final ArrayList available, + final ArrayList localOnly, + final ArrayList tethered, + final ArrayList errored) { + final Intent bcast = new Intent(ACTION_TETHER_STATE_CHANGED); + bcast.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); + + bcast.putStringArrayListExtra(EXTRA_AVAILABLE_TETHER, toIfaces(available)); + bcast.putStringArrayListExtra(EXTRA_ACTIVE_LOCAL_ONLY, toIfaces(localOnly)); + bcast.putStringArrayListExtra(EXTRA_ACTIVE_TETHER, toIfaces(tethered)); + bcast.putStringArrayListExtra(EXTRA_ERRORED_TETHER, toIfaces(errored)); + + return bcast; + } + private class StateReceiver extends BroadcastReceiver { @Override public void onReceive(Context content, Intent intent) { @@ -2082,10 +2107,10 @@ public class Tethering { private TetherStatesParcel emptyTetherStatesParcel() { final TetherStatesParcel parcel = new TetherStatesParcel(); - parcel.availableList = new String[0]; - parcel.tetheredList = new String[0]; - parcel.localOnlyList = new String[0]; - parcel.erroredIfaceList = new String[0]; + parcel.availableList = new TetheringInterface[0]; + parcel.tetheredList = new TetheringInterface[0]; + parcel.localOnlyList = new TetheringInterface[0]; + parcel.erroredIfaceList = new TetheringInterface[0]; parcel.lastErrorList = new int[0]; return parcel; diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java index 69c17583a0..2b158665cc 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java @@ -132,6 +132,7 @@ import android.net.TetheredClient; import android.net.TetheredClient.AddressInfo; import android.net.TetheringCallbackStartedParcel; import android.net.TetheringConfigurationParcel; +import android.net.TetheringInterface; import android.net.TetheringRequestParcel; import android.net.dhcp.DhcpLeaseParcelable; import android.net.dhcp.DhcpServerCallbacks; @@ -1779,6 +1780,8 @@ public class TetheringTest { public void testRegisterTetheringEventCallback() throws Exception { TestTetheringEventCallback callback = new TestTetheringEventCallback(); TestTetheringEventCallback callback2 = new TestTetheringEventCallback(); + final TetheringInterface wifiIface = new TetheringInterface( + TETHERING_WIFI, TEST_WLAN_IFNAME); // 1. Register one callback before running any tethering. mTethering.registerTetheringEventCallback(callback); @@ -1797,12 +1800,12 @@ public class TetheringTest { mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true); mLooper.dispatchAll(); tetherState = callback.pollTetherStatesChanged(); - assertArrayEquals(tetherState.availableList, new String[] {TEST_WLAN_IFNAME}); + assertArrayEquals(tetherState.availableList, new TetheringInterface[] {wifiIface}); mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null); sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED); tetherState = callback.pollTetherStatesChanged(); - assertArrayEquals(tetherState.tetheredList, new String[] {TEST_WLAN_IFNAME}); + assertArrayEquals(tetherState.tetheredList, new TetheringInterface[] {wifiIface}); callback.expectUpstreamChanged(upstreamState.network); callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STARTED); @@ -1814,7 +1817,7 @@ public class TetheringTest { callback2.expectConfigurationChanged( mTethering.getTetheringConfiguration().toStableParcelable()); tetherState = callback2.pollTetherStatesChanged(); - assertEquals(tetherState.tetheredList, new String[] {TEST_WLAN_IFNAME}); + assertEquals(tetherState.tetheredList, new TetheringInterface[] {wifiIface}); callback2.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STARTED); // 4. Unregister first callback and disable wifi tethering @@ -1823,7 +1826,7 @@ public class TetheringTest { mTethering.stopTethering(TETHERING_WIFI); sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED); tetherState = callback2.pollTetherStatesChanged(); - assertArrayEquals(tetherState.availableList, new String[] {TEST_WLAN_IFNAME}); + assertArrayEquals(tetherState.availableList, new TetheringInterface[] {wifiIface}); mLooper.dispatchAll(); callback2.expectUpstreamChanged(new Network[] {null}); callback2.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED);