From cf7d9b2f23376ea85ea9a59db023f0e2e382c77d Mon Sep 17 00:00:00 2001 From: markchien Date: Mon, 23 Sep 2019 20:29:54 +0800 Subject: [PATCH 001/188] [Tether04] Migrate EntitlementManager into module Bug: 136040414 Test: -build, flash, boot -atest TetheringTests -atest FrameworksNetTests Change-Id: Ifdfc6cd95377351c37946a146b60896f07ece59d --- Tethering/Android.bp | 11 +- .../tethering/EntitlementManager.java | 678 ++++++++++++++++++ Tethering/tests/unit/Android.bp | 6 +- .../tethering/EntitlementManagerTest.java | 508 +++++++++++++ 4 files changed, 1201 insertions(+), 2 deletions(-) create mode 100644 Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java create mode 100644 Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java diff --git a/Tethering/Android.bp b/Tethering/Android.bp index ca69c18708..2bfe287c82 100644 --- a/Tethering/Android.bp +++ b/Tethering/Android.bp @@ -21,6 +21,7 @@ java_defaults { "src/**/*.java", ":framework-tethering-shared-srcs", ":services-tethering-shared-srcs", + ":servicescore-tethering-src", ], static_libs: [ "androidx.annotation_annotation", @@ -67,9 +68,17 @@ android_app { // This group will be removed when tethering migration is done. filegroup { - name: "tethering-services-srcs", + name: "tethering-servicescore-srcs", srcs: [ + "src/com/android/server/connectivity/tethering/EntitlementManager.java", "src/com/android/server/connectivity/tethering/TetheringConfiguration.java", + ], +} + +// This group will be removed when tethering migration is done. +filegroup { + name: "tethering-servicesnet-srcs", + srcs: [ "src/android/net/dhcp/DhcpServerCallbacks.java", "src/android/net/dhcp/DhcpServingParamsParcelExt.java", "src/android/net/ip/IpServer.java", diff --git a/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java b/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java new file mode 100644 index 0000000000..6b0f1de7ce --- /dev/null +++ b/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java @@ -0,0 +1,678 @@ +/* + * 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.connectivity.tethering; + +import static android.net.ConnectivityManager.EXTRA_ADD_TETHER_TYPE; +import static android.net.ConnectivityManager.EXTRA_PROVISION_CALLBACK; +import static android.net.ConnectivityManager.EXTRA_RUN_PROVISION; +import static android.net.ConnectivityManager.TETHERING_BLUETOOTH; +import static android.net.ConnectivityManager.TETHERING_INVALID; +import static android.net.ConnectivityManager.TETHERING_USB; +import static android.net.ConnectivityManager.TETHERING_WIFI; +import static android.net.ConnectivityManager.TETHER_ERROR_ENTITLEMENT_UNKONWN; +import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR; +import static android.net.ConnectivityManager.TETHER_ERROR_PROVISION_FAILED; + +import static com.android.internal.R.string.config_wifi_tether_enable; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.res.Resources; +import android.net.util.SharedLog; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.Parcel; +import android.os.PersistableBundle; +import android.os.ResultReceiver; +import android.os.SystemClock; +import android.os.UserHandle; +import android.provider.Settings; +import android.telephony.CarrierConfigManager; +import android.util.ArraySet; +import android.util.SparseIntArray; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.StateMachine; +import com.android.server.connectivity.MockableSystemProperties; + +import java.io.PrintWriter; + +/** + * Re-check tethering provisioning for enabled downstream tether types. + * Reference ConnectivityManager.TETHERING_{@code *} for each tether type. + * + * All methods of this class must be accessed from the thread of tethering + * state machine. + * @hide + */ +public class EntitlementManager { + private static final String TAG = EntitlementManager.class.getSimpleName(); + private static final boolean DBG = false; + + @VisibleForTesting + protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning"; + private static final String ACTION_PROVISIONING_ALARM = + "com.android.server.connectivity.tethering.PROVISIONING_RECHECK_ALARM"; + private static final String EXTRA_SUBID = "subId"; + + // {@link ComponentName} of the Service used to run tether provisioning. + private static final ComponentName TETHER_SERVICE = ComponentName.unflattenFromString( + Resources.getSystem().getString(config_wifi_tether_enable)); + private static final int MS_PER_HOUR = 60 * 60 * 1000; + private static final int EVENT_START_PROVISIONING = 0; + private static final int EVENT_STOP_PROVISIONING = 1; + private static final int EVENT_UPSTREAM_CHANGED = 2; + private static final int EVENT_MAYBE_RUN_PROVISIONING = 3; + private static final int EVENT_GET_ENTITLEMENT_VALUE = 4; + + // The ArraySet contains enabled downstream types, ex: + // {@link ConnectivityManager.TETHERING_WIFI} + // {@link ConnectivityManager.TETHERING_USB} + // {@link ConnectivityManager.TETHERING_BLUETOOTH} + private final ArraySet mCurrentTethers; + private final Context mContext; + private final int mPermissionChangeMessageCode; + private final MockableSystemProperties mSystemProperties; + private final SharedLog mLog; + private final SparseIntArray mEntitlementCacheValue; + private final EntitlementHandler mHandler; + private final StateMachine mTetherMasterSM; + // Key: ConnectivityManager.TETHERING_*(downstream). + // Value: ConnectivityManager.TETHER_ERROR_{NO_ERROR or PROVISION_FAILED}(provisioning result). + private final SparseIntArray mCellularPermitted; + private PendingIntent mProvisioningRecheckAlarm; + private boolean mCellularUpstreamPermitted = true; + private boolean mUsingCellularAsUpstream = false; + private boolean mNeedReRunProvisioningUi = false; + private OnUiEntitlementFailedListener mListener; + private TetheringConfigurationFetcher mFetcher; + + public EntitlementManager(Context ctx, StateMachine tetherMasterSM, SharedLog log, + int permissionChangeMessageCode, MockableSystemProperties systemProperties) { + mContext = ctx; + mLog = log.forSubComponent(TAG); + mCurrentTethers = new ArraySet(); + mCellularPermitted = new SparseIntArray(); + mSystemProperties = systemProperties; + mEntitlementCacheValue = new SparseIntArray(); + mTetherMasterSM = tetherMasterSM; + mPermissionChangeMessageCode = permissionChangeMessageCode; + final Handler masterHandler = tetherMasterSM.getHandler(); + // Create entitlement's own handler which is associated with TetherMaster thread + // let all entitlement processes run in the same thread. + mHandler = new EntitlementHandler(masterHandler.getLooper()); + mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_PROVISIONING_ALARM), + null, mHandler); + } + + public void setOnUiEntitlementFailedListener(final OnUiEntitlementFailedListener listener) { + mListener = listener; + } + + /** Callback fired when UI entitlement failed. */ + public interface OnUiEntitlementFailedListener { + /** + * Ui entitlement check fails in |downstream|. + * + * @param downstream tethering type from ConnectivityManager.TETHERING_{@code *}. + */ + void onUiEntitlementFailed(int downstream); + } + + public void setTetheringConfigurationFetcher(final TetheringConfigurationFetcher fetcher) { + mFetcher = fetcher; + } + + /** Interface to fetch TetheringConfiguration. */ + public interface TetheringConfigurationFetcher { + /** + * Fetch current tethering configuration. This will be called to ensure whether entitlement + * check is needed. + * @return TetheringConfiguration instance. + */ + TetheringConfiguration fetchTetheringConfiguration(); + } + + /** + * Check if cellular upstream is permitted. + */ + public boolean isCellularUpstreamPermitted() { + // If provisioning is required and EntitlementManager don't know any downstream, + // cellular upstream should not be allowed. + final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); + if (mCurrentTethers.size() == 0 && isTetherProvisioningRequired(config)) { + return false; + } + return mCellularUpstreamPermitted; + } + + /** + * This is called when tethering starts. + * Launch provisioning app if upstream is cellular. + * + * @param downstreamType tethering type from ConnectivityManager.TETHERING_{@code *} + * @param showProvisioningUi a boolean indicating whether to show the + * provisioning app UI if there is one. + */ + public void startProvisioningIfNeeded(int downstreamType, boolean showProvisioningUi) { + mHandler.sendMessage(mHandler.obtainMessage(EVENT_START_PROVISIONING, + downstreamType, encodeBool(showProvisioningUi))); + } + + private void handleStartProvisioningIfNeeded(int type, boolean showProvisioningUi) { + if (!isValidDownstreamType(type)) return; + + if (!mCurrentTethers.contains(type)) mCurrentTethers.add(type); + + final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); + if (isTetherProvisioningRequired(config)) { + // If provisioning is required and the result is not available yet, + // cellular upstream should not be allowed. + if (mCellularPermitted.size() == 0) { + mCellularUpstreamPermitted = false; + } + // If upstream is not cellular, provisioning app would not be launched + // till upstream change to cellular. + if (mUsingCellularAsUpstream) { + if (showProvisioningUi) { + runUiTetherProvisioning(type, config.subId); + } else { + runSilentTetherProvisioning(type, config.subId); + } + mNeedReRunProvisioningUi = false; + } else { + mNeedReRunProvisioningUi |= showProvisioningUi; + } + } else { + mCellularUpstreamPermitted = true; + } + } + + /** + * Tell EntitlementManager that a given type of tethering has been disabled + * + * @param type tethering type from ConnectivityManager.TETHERING_{@code *} + */ + public void stopProvisioningIfNeeded(int type) { + mHandler.sendMessage(mHandler.obtainMessage(EVENT_STOP_PROVISIONING, type, 0)); + } + + private void handleStopProvisioningIfNeeded(int type) { + if (!isValidDownstreamType(type)) return; + + mCurrentTethers.remove(type); + // There are lurking bugs where the notion of "provisioning required" or + // "tethering supported" may change without without tethering being notified properly. + // Remove the mapping all the time no matter provisioning is required or not. + removeDownstreamMapping(type); + } + + /** + * Notify EntitlementManager if upstream is cellular or not. + * + * @param isCellular whether tethering upstream is cellular. + */ + public void notifyUpstream(boolean isCellular) { + mHandler.sendMessage(mHandler.obtainMessage( + EVENT_UPSTREAM_CHANGED, encodeBool(isCellular), 0)); + } + + private void handleNotifyUpstream(boolean isCellular) { + if (DBG) { + mLog.i("notifyUpstream: " + isCellular + + ", mCellularUpstreamPermitted: " + mCellularUpstreamPermitted + + ", mNeedReRunProvisioningUi: " + mNeedReRunProvisioningUi); + } + mUsingCellularAsUpstream = isCellular; + + if (mUsingCellularAsUpstream) { + final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); + handleMaybeRunProvisioning(config); + } + } + + /** Run provisioning if needed */ + public void maybeRunProvisioning() { + mHandler.sendMessage(mHandler.obtainMessage(EVENT_MAYBE_RUN_PROVISIONING)); + } + + private void handleMaybeRunProvisioning(final TetheringConfiguration config) { + if (mCurrentTethers.size() == 0 || !isTetherProvisioningRequired(config)) { + return; + } + + // Whenever any entitlement value changes, all downstreams will re-evaluate whether they + // are allowed. Therefore even if the silent check here ends in a failure and the UI later + // yields success, then the downstream that got a failure will re-evaluate as a result of + // the change and get the new correct value. + for (Integer downstream : mCurrentTethers) { + if (mCellularPermitted.indexOfKey(downstream) < 0) { + if (mNeedReRunProvisioningUi) { + mNeedReRunProvisioningUi = false; + runUiTetherProvisioning(downstream, config.subId); + } else { + runSilentTetherProvisioning(downstream, config.subId); + } + } + } + } + + /** + * Check if the device requires a provisioning check in order to enable tethering. + * + * @param config an object that encapsulates the various tethering configuration elements. + * @return a boolean - {@code true} indicating tether provisioning is required by the carrier. + */ + @VisibleForTesting + protected boolean isTetherProvisioningRequired(final TetheringConfiguration config) { + if (mSystemProperties.getBoolean(DISABLE_PROVISIONING_SYSPROP_KEY, false) + || config.provisioningApp.length == 0) { + return false; + } + if (carrierConfigAffirmsEntitlementCheckNotRequired(config)) { + return false; + } + return (config.provisioningApp.length == 2); + } + + /** + * Re-check tethering provisioning for all enabled tether types. + * Reference ConnectivityManager.TETHERING_{@code *} for each tether type. + * + * @param config an object that encapsulates the various tethering configuration elements. + * Note: this method is only called from TetherMaster on the handler thread. + * If there are new callers from different threads, the logic should move to + * masterHandler to avoid race conditions. + */ + public void reevaluateSimCardProvisioning(final TetheringConfiguration config) { + if (DBG) mLog.i("reevaluateSimCardProvisioning"); + + if (!mHandler.getLooper().isCurrentThread()) { + // Except for test, this log should not appear in normal flow. + mLog.log("reevaluateSimCardProvisioning() don't run in TetherMaster thread"); + } + mEntitlementCacheValue.clear(); + mCellularPermitted.clear(); + + // TODO: refine provisioning check to isTetherProvisioningRequired() ?? + if (!config.hasMobileHotspotProvisionApp() + || carrierConfigAffirmsEntitlementCheckNotRequired(config)) { + evaluateCellularPermission(config); + return; + } + + if (mUsingCellularAsUpstream) { + handleMaybeRunProvisioning(config); + } + } + + /** + * Get carrier configuration bundle. + * @param config an object that encapsulates the various tethering configuration elements. + * */ + public PersistableBundle getCarrierConfig(final TetheringConfiguration config) { + final CarrierConfigManager configManager = (CarrierConfigManager) mContext + .getSystemService(Context.CARRIER_CONFIG_SERVICE); + if (configManager == null) return null; + + final PersistableBundle carrierConfig = configManager.getConfigForSubId(config.subId); + + if (CarrierConfigManager.isConfigForIdentifiedCarrier(carrierConfig)) { + return carrierConfig; + } + + return null; + } + + // The logic here is aimed solely at confirming that a CarrierConfig exists + // and affirms that entitlement checks are not required. + // + // TODO: find a better way to express this, or alter the checking process + // entirely so that this is more intuitive. + private boolean carrierConfigAffirmsEntitlementCheckNotRequired( + final TetheringConfiguration config) { + // Check carrier config for entitlement checks + final PersistableBundle carrierConfig = getCarrierConfig(config); + if (carrierConfig == null) return false; + + // A CarrierConfigManager was found and it has a config. + final boolean isEntitlementCheckRequired = carrierConfig.getBoolean( + CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL); + return !isEntitlementCheckRequired; + } + + /** + * Run no UI tethering provisioning check. + * @param type tethering type from ConnectivityManager.TETHERING_{@code *} + * @param subId default data subscription ID. + */ + @VisibleForTesting + protected void runSilentTetherProvisioning(int type, int subId) { + if (DBG) mLog.i("runSilentTetherProvisioning: " + type); + // For silent provisioning, settings would stop tethering when entitlement fail. + ResultReceiver receiver = buildProxyReceiver(type, false/* notifyFail */, null); + + Intent intent = new Intent(); + intent.putExtra(EXTRA_ADD_TETHER_TYPE, type); + intent.putExtra(EXTRA_RUN_PROVISION, true); + intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver); + intent.putExtra(EXTRA_SUBID, subId); + intent.setComponent(TETHER_SERVICE); + final long ident = Binder.clearCallingIdentity(); + try { + mContext.startServiceAsUser(intent, UserHandle.CURRENT); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + private void runUiTetherProvisioning(int type, int subId) { + ResultReceiver receiver = buildProxyReceiver(type, true/* notifyFail */, null); + runUiTetherProvisioning(type, subId, receiver); + } + + /** + * Run the UI-enabled tethering provisioning check. + * @param type tethering type from ConnectivityManager.TETHERING_{@code *} + * @param subId default data subscription ID. + * @param receiver to receive entitlement check result. + */ + @VisibleForTesting + protected void runUiTetherProvisioning(int type, int subId, ResultReceiver receiver) { + if (DBG) mLog.i("runUiTetherProvisioning: " + type); + + Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING); + intent.putExtra(EXTRA_ADD_TETHER_TYPE, type); + intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver); + intent.putExtra(EXTRA_SUBID, subId); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + final long ident = Binder.clearCallingIdentity(); + try { + mContext.startActivityAsUser(intent, UserHandle.CURRENT); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + // Not needed to check if this don't run on the handler thread because it's private. + private void scheduleProvisioningRechecks(final TetheringConfiguration config) { + if (mProvisioningRecheckAlarm == null) { + final int period = config.provisioningCheckPeriod; + if (period <= 0) return; + + Intent intent = new Intent(ACTION_PROVISIONING_ALARM); + mProvisioningRecheckAlarm = PendingIntent.getBroadcast(mContext, 0, intent, 0); + AlarmManager alarmManager = (AlarmManager) mContext.getSystemService( + Context.ALARM_SERVICE); + long periodMs = period * MS_PER_HOUR; + long firstAlarmTime = SystemClock.elapsedRealtime() + periodMs; + alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, firstAlarmTime, periodMs, + mProvisioningRecheckAlarm); + } + } + + private void cancelTetherProvisioningRechecks() { + if (mProvisioningRecheckAlarm != null) { + AlarmManager alarmManager = (AlarmManager) mContext.getSystemService( + Context.ALARM_SERVICE); + alarmManager.cancel(mProvisioningRecheckAlarm); + mProvisioningRecheckAlarm = null; + } + } + + private void evaluateCellularPermission(final TetheringConfiguration config) { + final boolean oldPermitted = mCellularUpstreamPermitted; + mCellularUpstreamPermitted = (!isTetherProvisioningRequired(config) + || mCellularPermitted.indexOfValue(TETHER_ERROR_NO_ERROR) > -1); + + if (DBG) { + mLog.i("Cellular permission change from " + oldPermitted + + " to " + mCellularUpstreamPermitted); + } + + if (mCellularUpstreamPermitted != oldPermitted) { + mLog.log("Cellular permission change: " + mCellularUpstreamPermitted); + mTetherMasterSM.sendMessage(mPermissionChangeMessageCode); + } + // Only schedule periodic re-check when tether is provisioned + // and the result is ok. + if (mCellularUpstreamPermitted && mCellularPermitted.size() > 0) { + scheduleProvisioningRechecks(config); + } else { + cancelTetherProvisioningRechecks(); + } + } + + /** + * Add the mapping between provisioning result and tethering type. + * Notify UpstreamNetworkMonitor if Cellular permission changes. + * + * @param type tethering type from ConnectivityManager.TETHERING_{@code *} + * @param resultCode Provisioning result + */ + protected void addDownstreamMapping(int type, int resultCode) { + mLog.i("addDownstreamMapping: " + type + ", result: " + resultCode + + " ,TetherTypeRequested: " + mCurrentTethers.contains(type)); + if (!mCurrentTethers.contains(type)) return; + + mCellularPermitted.put(type, resultCode); + final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); + evaluateCellularPermission(config); + } + + /** + * Remove the mapping for input tethering type. + * @param type tethering type from ConnectivityManager.TETHERING_{@code *} + */ + protected void removeDownstreamMapping(int type) { + mLog.i("removeDownstreamMapping: " + type); + mCellularPermitted.delete(type); + final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); + evaluateCellularPermission(config); + } + + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (ACTION_PROVISIONING_ALARM.equals(intent.getAction())) { + mLog.log("Received provisioning alarm"); + final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); + reevaluateSimCardProvisioning(config); + } + } + }; + + private class EntitlementHandler extends Handler { + EntitlementHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case EVENT_START_PROVISIONING: + handleStartProvisioningIfNeeded(msg.arg1, toBool(msg.arg2)); + break; + case EVENT_STOP_PROVISIONING: + handleStopProvisioningIfNeeded(msg.arg1); + break; + case EVENT_UPSTREAM_CHANGED: + handleNotifyUpstream(toBool(msg.arg1)); + break; + case EVENT_MAYBE_RUN_PROVISIONING: + final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); + handleMaybeRunProvisioning(config); + break; + case EVENT_GET_ENTITLEMENT_VALUE: + handleGetLatestTetheringEntitlementValue(msg.arg1, (ResultReceiver) msg.obj, + toBool(msg.arg2)); + break; + default: + mLog.log("Unknown event: " + msg.what); + break; + } + } + } + + private static boolean toBool(int encodedBoolean) { + return encodedBoolean != 0; + } + + private static int encodeBool(boolean b) { + return b ? 1 : 0; + } + + private static boolean isValidDownstreamType(int type) { + switch (type) { + case TETHERING_BLUETOOTH: + case TETHERING_USB: + case TETHERING_WIFI: + return true; + default: + return false; + } + } + + /** + * Dump the infromation of EntitlementManager. + * @param pw {@link PrintWriter} is used to print formatted + */ + public void dump(PrintWriter pw) { + pw.print("mCellularUpstreamPermitted: "); + pw.println(mCellularUpstreamPermitted); + for (Integer type : mCurrentTethers) { + pw.print("Type: "); + pw.print(typeString(type)); + if (mCellularPermitted.indexOfKey(type) > -1) { + pw.print(", Value: "); + pw.println(errorString(mCellularPermitted.get(type))); + } else { + pw.println(", Value: empty"); + } + } + } + + private static String typeString(int type) { + switch (type) { + case TETHERING_BLUETOOTH: return "TETHERING_BLUETOOTH"; + case TETHERING_INVALID: return "TETHERING_INVALID"; + case TETHERING_USB: return "TETHERING_USB"; + case TETHERING_WIFI: return "TETHERING_WIFI"; + default: + return String.format("TETHERING UNKNOWN TYPE (%d)", type); + } + } + + private static String errorString(int value) { + switch (value) { + case TETHER_ERROR_ENTITLEMENT_UNKONWN: return "TETHER_ERROR_ENTITLEMENT_UNKONWN"; + case TETHER_ERROR_NO_ERROR: return "TETHER_ERROR_NO_ERROR"; + case TETHER_ERROR_PROVISION_FAILED: return "TETHER_ERROR_PROVISION_FAILED"; + default: + return String.format("UNKNOWN ERROR (%d)", value); + } + } + + private ResultReceiver buildProxyReceiver(int type, boolean notifyFail, + final ResultReceiver receiver) { + ResultReceiver rr = new ResultReceiver(mHandler) { + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + int updatedCacheValue = updateEntitlementCacheValue(type, resultCode); + addDownstreamMapping(type, updatedCacheValue); + if (updatedCacheValue == TETHER_ERROR_PROVISION_FAILED && notifyFail) { + mListener.onUiEntitlementFailed(type); + } + if (receiver != null) receiver.send(updatedCacheValue, null); + } + }; + + return writeToParcel(rr); + } + + // Instances of ResultReceiver need to be public classes for remote processes to be able + // to load them (otherwise, ClassNotFoundException). For private classes, this method + // performs a trick : round-trip parceling any instance of ResultReceiver will return a + // vanilla instance of ResultReceiver sharing the binder token with the original receiver. + // The binder token has a reference to the original instance of the private class and will + // still call its methods, and can be sent over. However it cannot be used for anything + // else than sending over a Binder call. + // While round-trip parceling is not great, there is currently no other way of generating + // a vanilla instance of ResultReceiver because all its fields are private. + private ResultReceiver writeToParcel(final ResultReceiver receiver) { + Parcel parcel = Parcel.obtain(); + receiver.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + ResultReceiver receiverForSending = ResultReceiver.CREATOR.createFromParcel(parcel); + parcel.recycle(); + return receiverForSending; + } + + /** + * Update the last entitlement value to internal cache + * + * @param type tethering type from ConnectivityManager.TETHERING_{@code *} + * @param resultCode last entitlement value + * @return the last updated entitlement value + */ + private int updateEntitlementCacheValue(int type, int resultCode) { + if (DBG) { + mLog.i("updateEntitlementCacheValue: " + type + ", result: " + resultCode); + } + if (resultCode == TETHER_ERROR_NO_ERROR) { + mEntitlementCacheValue.put(type, resultCode); + return resultCode; + } else { + mEntitlementCacheValue.put(type, TETHER_ERROR_PROVISION_FAILED); + return TETHER_ERROR_PROVISION_FAILED; + } + } + + /** Get the last value of the tethering entitlement check. */ + public void getLatestTetheringEntitlementResult(int downstream, ResultReceiver receiver, + boolean showEntitlementUi) { + mHandler.sendMessage(mHandler.obtainMessage(EVENT_GET_ENTITLEMENT_VALUE, + downstream, encodeBool(showEntitlementUi), receiver)); + + } + + private void handleGetLatestTetheringEntitlementValue(int downstream, ResultReceiver receiver, + boolean showEntitlementUi) { + final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); + if (!isTetherProvisioningRequired(config)) { + receiver.send(TETHER_ERROR_NO_ERROR, null); + return; + } + + final int cacheValue = mEntitlementCacheValue.get( + downstream, TETHER_ERROR_ENTITLEMENT_UNKONWN); + if (cacheValue == TETHER_ERROR_NO_ERROR || !showEntitlementUi) { + receiver.send(cacheValue, null); + } else { + ResultReceiver proxy = buildProxyReceiver(downstream, false/* notifyFail */, receiver); + runUiTetherProvisioning(downstream, config.subId, proxy); + } + } +} diff --git a/Tethering/tests/unit/Android.bp b/Tethering/tests/unit/Android.bp index da621076bb..5564bd6ecb 100644 --- a/Tethering/tests/unit/Android.bp +++ b/Tethering/tests/unit/Android.bp @@ -17,7 +17,10 @@ android_test { name: "TetheringTests", certificate: "platform", - srcs: ["src/**/*.java"], + srcs: [ + ":servicescore-tethering-src", + "src/**/*.java", + ], test_suites: ["device-tests"], static_libs: [ "androidx.test.rules", @@ -42,6 +45,7 @@ android_test { filegroup { name: "tethering-tests-src", srcs: [ + "src/com/android/server/connectivity/tethering/EntitlementManagerTest.java", "src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java", "src/android/net/dhcp/DhcpServingParamsParcelExtTest.java", "src/android/net/ip/IpServerTest.java", diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java new file mode 100644 index 0000000000..5217e26a40 --- /dev/null +++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java @@ -0,0 +1,508 @@ +/* + * 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.connectivity.tethering; + +import static android.net.ConnectivityManager.TETHERING_BLUETOOTH; +import static android.net.ConnectivityManager.TETHERING_USB; +import static android.net.ConnectivityManager.TETHERING_WIFI; +import static android.net.ConnectivityManager.TETHER_ERROR_ENTITLEMENT_UNKONWN; +import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR; +import static android.net.ConnectivityManager.TETHER_ERROR_PROVISION_FAILED; +import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.res.Resources; +import android.net.util.SharedLog; +import android.os.Bundle; +import android.os.Message; +import android.os.PersistableBundle; +import android.os.ResultReceiver; +import android.os.test.TestLooper; +import android.provider.Settings; +import android.telephony.CarrierConfigManager; +import android.test.mock.MockContentResolver; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.R; +import com.android.internal.util.State; +import com.android.internal.util.StateMachine; +import com.android.internal.util.test.BroadcastInterceptingContext; +import com.android.internal.util.test.FakeSettingsProvider; +import com.android.server.connectivity.MockableSystemProperties; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public final class EntitlementManagerTest { + + private static final int EVENT_EM_UPDATE = 1; + private static final String[] PROVISIONING_APP_NAME = {"some", "app"}; + private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app"; + + @Mock private CarrierConfigManager mCarrierConfigManager; + @Mock private Context mContext; + @Mock private MockableSystemProperties mSystemProperties; + @Mock private Resources mResources; + @Mock private SharedLog mLog; + @Mock private EntitlementManager.OnUiEntitlementFailedListener mEntitlementFailedListener; + + // Like so many Android system APIs, these cannot be mocked because it is marked final. + // We have to use the real versions. + private final PersistableBundle mCarrierConfig = new PersistableBundle(); + private final TestLooper mLooper = new TestLooper(); + private Context mMockContext; + private MockContentResolver mContentResolver; + + private TestStateMachine mSM; + private WrappedEntitlementManager mEnMgr; + private TetheringConfiguration mConfig; + + private class MockContext extends BroadcastInterceptingContext { + MockContext(Context base) { + super(base); + } + + @Override + public Resources getResources() { + return mResources; + } + + @Override + public ContentResolver getContentResolver() { + return mContentResolver; + } + } + + public class WrappedEntitlementManager extends EntitlementManager { + public int fakeEntitlementResult = TETHER_ERROR_ENTITLEMENT_UNKONWN; + public int uiProvisionCount = 0; + public int silentProvisionCount = 0; + + public WrappedEntitlementManager(Context ctx, StateMachine target, + SharedLog log, int what, MockableSystemProperties systemProperties) { + super(ctx, target, log, what, systemProperties); + } + + public void reset() { + fakeEntitlementResult = TETHER_ERROR_ENTITLEMENT_UNKONWN; + uiProvisionCount = 0; + silentProvisionCount = 0; + } + + @Override + protected void runUiTetherProvisioning(int type, int subId, ResultReceiver receiver) { + uiProvisionCount++; + receiver.send(fakeEntitlementResult, null); + } + + @Override + protected void runSilentTetherProvisioning(int type, int subId) { + silentProvisionCount++; + addDownstreamMapping(type, fakeEntitlementResult); + } + } + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + when(mResources.getStringArray(R.array.config_tether_dhcp_range)) + .thenReturn(new String[0]); + when(mResources.getStringArray(R.array.config_tether_usb_regexs)) + .thenReturn(new String[0]); + when(mResources.getStringArray(R.array.config_tether_wifi_regexs)) + .thenReturn(new String[0]); + when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs)) + .thenReturn(new String[0]); + when(mResources.getIntArray(R.array.config_tether_upstream_types)) + .thenReturn(new int[0]); + when(mLog.forSubComponent(anyString())).thenReturn(mLog); + + mContentResolver = new MockContentResolver(); + mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); + mMockContext = new MockContext(mContext); + mSM = new TestStateMachine(); + mEnMgr = new WrappedEntitlementManager(mMockContext, mSM, mLog, EVENT_EM_UPDATE, + mSystemProperties); + mEnMgr.setOnUiEntitlementFailedListener(mEntitlementFailedListener); + mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); + mEnMgr.setTetheringConfigurationFetcher(() -> { + return mConfig; + }); + } + + @After + public void tearDown() throws Exception { + if (mSM != null) { + mSM.quit(); + mSM = null; + } + } + + private void setupForRequiredProvisioning() { + // Produce some acceptable looking provision app setting if requested. + when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app)) + .thenReturn(PROVISIONING_APP_NAME); + when(mResources.getString(R.string.config_mobile_hotspot_provision_app_no_ui)) + .thenReturn(PROVISIONING_NO_UI_APP_NAME); + // Don't disable tethering provisioning unless requested. + when(mSystemProperties.getBoolean(eq(EntitlementManager.DISABLE_PROVISIONING_SYSPROP_KEY), + anyBoolean())).thenReturn(false); + // Act like the CarrierConfigManager is present and ready unless told otherwise. + when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE)) + .thenReturn(mCarrierConfigManager); + when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(mCarrierConfig); + mCarrierConfig.putBoolean(CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, true); + mCarrierConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true); + mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); + } + + @Test + public void canRequireProvisioning() { + setupForRequiredProvisioning(); + assertTrue(mEnMgr.isTetherProvisioningRequired(mConfig)); + } + + @Test + public void toleratesCarrierConfigManagerMissing() { + setupForRequiredProvisioning(); + when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE)) + .thenReturn(null); + mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); + // Couldn't get the CarrierConfigManager, but still had a declared provisioning app. + // Therefore provisioning still be required. + assertTrue(mEnMgr.isTetherProvisioningRequired(mConfig)); + } + + @Test + public void toleratesCarrierConfigMissing() { + setupForRequiredProvisioning(); + when(mCarrierConfigManager.getConfig()).thenReturn(null); + mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); + // We still have a provisioning app configured, so still require provisioning. + assertTrue(mEnMgr.isTetherProvisioningRequired(mConfig)); + } + + @Test + public void toleratesCarrierConfigNotLoaded() { + setupForRequiredProvisioning(); + mCarrierConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, false); + // We still have a provisioning app configured, so still require provisioning. + assertTrue(mEnMgr.isTetherProvisioningRequired(mConfig)); + } + + @Test + public void provisioningNotRequiredWhenAppNotFound() { + setupForRequiredProvisioning(); + when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app)) + .thenReturn(null); + mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); + assertFalse(mEnMgr.isTetherProvisioningRequired(mConfig)); + when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app)) + .thenReturn(new String[] {"malformedApp"}); + mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); + assertFalse(mEnMgr.isTetherProvisioningRequired(mConfig)); + } + + @Test + public void testGetLastEntitlementCacheValue() throws Exception { + final CountDownLatch mCallbacklatch = new CountDownLatch(1); + // 1. Entitlement check is not required. + mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; + ResultReceiver receiver = new ResultReceiver(null) { + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + assertEquals(TETHER_ERROR_NO_ERROR, resultCode); + mCallbacklatch.countDown(); + } + }; + mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true); + mLooper.dispatchAll(); + callbackTimeoutHelper(mCallbacklatch); + assertEquals(0, mEnMgr.uiProvisionCount); + mEnMgr.reset(); + + setupForRequiredProvisioning(); + // 2. No cache value and don't need to run entitlement check. + receiver = new ResultReceiver(null) { + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + assertEquals(TETHER_ERROR_ENTITLEMENT_UNKONWN, resultCode); + mCallbacklatch.countDown(); + } + }; + mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false); + mLooper.dispatchAll(); + callbackTimeoutHelper(mCallbacklatch); + assertEquals(0, mEnMgr.uiProvisionCount); + mEnMgr.reset(); + // 3. No cache value and ui entitlement check is needed. + mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED; + receiver = new ResultReceiver(null) { + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + assertEquals(TETHER_ERROR_PROVISION_FAILED, resultCode); + mCallbacklatch.countDown(); + } + }; + mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true); + mLooper.dispatchAll(); + callbackTimeoutHelper(mCallbacklatch); + assertEquals(1, mEnMgr.uiProvisionCount); + mEnMgr.reset(); + // 4. Cache value is TETHER_ERROR_PROVISION_FAILED and don't need to run entitlement check. + mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; + receiver = new ResultReceiver(null) { + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + assertEquals(TETHER_ERROR_PROVISION_FAILED, resultCode); + mCallbacklatch.countDown(); + } + }; + mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false); + mLooper.dispatchAll(); + callbackTimeoutHelper(mCallbacklatch); + assertEquals(0, mEnMgr.uiProvisionCount); + mEnMgr.reset(); + // 5. Cache value is TETHER_ERROR_PROVISION_FAILED and ui entitlement check is needed. + mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; + receiver = new ResultReceiver(null) { + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + assertEquals(TETHER_ERROR_NO_ERROR, resultCode); + mCallbacklatch.countDown(); + } + }; + mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true); + mLooper.dispatchAll(); + callbackTimeoutHelper(mCallbacklatch); + assertEquals(1, mEnMgr.uiProvisionCount); + mEnMgr.reset(); + // 6. Cache value is TETHER_ERROR_NO_ERROR. + mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; + receiver = new ResultReceiver(null) { + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + assertEquals(TETHER_ERROR_NO_ERROR, resultCode); + mCallbacklatch.countDown(); + } + }; + mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true); + mLooper.dispatchAll(); + callbackTimeoutHelper(mCallbacklatch); + assertEquals(0, mEnMgr.uiProvisionCount); + mEnMgr.reset(); + // 7. Test get value for other downstream type. + receiver = new ResultReceiver(null) { + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + assertEquals(TETHER_ERROR_ENTITLEMENT_UNKONWN, resultCode); + mCallbacklatch.countDown(); + } + }; + mEnMgr.getLatestTetheringEntitlementResult(TETHERING_USB, receiver, false); + mLooper.dispatchAll(); + callbackTimeoutHelper(mCallbacklatch); + assertEquals(0, mEnMgr.uiProvisionCount); + mEnMgr.reset(); + } + + void callbackTimeoutHelper(final CountDownLatch latch) throws Exception { + if (!latch.await(1, TimeUnit.SECONDS)) { + fail("Timout, fail to receive callback"); + } + } + + @Test + public void verifyPermissionResult() { + setupForRequiredProvisioning(); + mEnMgr.notifyUpstream(true); + mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED; + mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true); + mLooper.dispatchAll(); + assertFalse(mEnMgr.isCellularUpstreamPermitted()); + mEnMgr.stopProvisioningIfNeeded(TETHERING_WIFI); + mLooper.dispatchAll(); + mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; + mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true); + mLooper.dispatchAll(); + assertTrue(mEnMgr.isCellularUpstreamPermitted()); + } + + @Test + public void verifyPermissionIfAllNotApproved() { + setupForRequiredProvisioning(); + mEnMgr.notifyUpstream(true); + mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED; + mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true); + mLooper.dispatchAll(); + assertFalse(mEnMgr.isCellularUpstreamPermitted()); + mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED; + mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true); + mLooper.dispatchAll(); + assertFalse(mEnMgr.isCellularUpstreamPermitted()); + mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED; + mEnMgr.startProvisioningIfNeeded(TETHERING_BLUETOOTH, true); + mLooper.dispatchAll(); + assertFalse(mEnMgr.isCellularUpstreamPermitted()); + } + + @Test + public void verifyPermissionIfAnyApproved() { + setupForRequiredProvisioning(); + mEnMgr.notifyUpstream(true); + mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; + mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true); + mLooper.dispatchAll(); + assertTrue(mEnMgr.isCellularUpstreamPermitted()); + mLooper.dispatchAll(); + mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED; + mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true); + mLooper.dispatchAll(); + assertTrue(mEnMgr.isCellularUpstreamPermitted()); + mEnMgr.stopProvisioningIfNeeded(TETHERING_WIFI); + mLooper.dispatchAll(); + assertFalse(mEnMgr.isCellularUpstreamPermitted()); + + } + + @Test + public void verifyPermissionWhenProvisioningNotStarted() { + assertTrue(mEnMgr.isCellularUpstreamPermitted()); + setupForRequiredProvisioning(); + assertFalse(mEnMgr.isCellularUpstreamPermitted()); + } + + @Test + public void testRunTetherProvisioning() { + setupForRequiredProvisioning(); + // 1. start ui provisioning, upstream is mobile + mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; + mEnMgr.notifyUpstream(true); + mLooper.dispatchAll(); + mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true); + mLooper.dispatchAll(); + assertEquals(1, mEnMgr.uiProvisionCount); + assertEquals(0, mEnMgr.silentProvisionCount); + assertTrue(mEnMgr.isCellularUpstreamPermitted()); + mEnMgr.reset(); + // 2. start no-ui provisioning + mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; + mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, false); + mLooper.dispatchAll(); + assertEquals(0, mEnMgr.uiProvisionCount); + assertEquals(1, mEnMgr.silentProvisionCount); + assertTrue(mEnMgr.isCellularUpstreamPermitted()); + mEnMgr.reset(); + // 3. tear down mobile, then start ui provisioning + mEnMgr.notifyUpstream(false); + mLooper.dispatchAll(); + mEnMgr.startProvisioningIfNeeded(TETHERING_BLUETOOTH, true); + mLooper.dispatchAll(); + assertEquals(0, mEnMgr.uiProvisionCount); + assertEquals(0, mEnMgr.silentProvisionCount); + mEnMgr.reset(); + // 4. switch upstream back to mobile + mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; + mEnMgr.notifyUpstream(true); + mLooper.dispatchAll(); + assertEquals(1, mEnMgr.uiProvisionCount); + assertEquals(0, mEnMgr.silentProvisionCount); + assertTrue(mEnMgr.isCellularUpstreamPermitted()); + mEnMgr.reset(); + // 5. tear down mobile, then switch SIM + mEnMgr.notifyUpstream(false); + mLooper.dispatchAll(); + mEnMgr.reevaluateSimCardProvisioning(mConfig); + assertEquals(0, mEnMgr.uiProvisionCount); + assertEquals(0, mEnMgr.silentProvisionCount); + mEnMgr.reset(); + // 6. switch upstream back to mobile again + mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED; + mEnMgr.notifyUpstream(true); + mLooper.dispatchAll(); + assertEquals(0, mEnMgr.uiProvisionCount); + assertEquals(3, mEnMgr.silentProvisionCount); + mEnMgr.reset(); + } + + @Test + public void testCallStopTetheringWhenUiProvisioningFail() { + setupForRequiredProvisioning(); + verify(mEntitlementFailedListener, times(0)).onUiEntitlementFailed(TETHERING_WIFI); + mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED; + mEnMgr.notifyUpstream(true); + mLooper.dispatchAll(); + mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true); + mLooper.dispatchAll(); + assertEquals(1, mEnMgr.uiProvisionCount); + verify(mEntitlementFailedListener, times(1)).onUiEntitlementFailed(TETHERING_WIFI); + } + + public class TestStateMachine extends StateMachine { + public final ArrayList messages = new ArrayList<>(); + private final State + mLoggingState = new EntitlementManagerTest.TestStateMachine.LoggingState(); + + class LoggingState extends State { + @Override public void enter() { + messages.clear(); + } + + @Override public void exit() { + messages.clear(); + } + + @Override public boolean processMessage(Message msg) { + messages.add(msg); + return false; + } + } + + public TestStateMachine() { + super("EntitlementManagerTest.TestStateMachine", mLooper.getLooper()); + addState(mLoggingState); + setInitialState(mLoggingState); + super.start(); + } + } +} From 87b8bf85c61c87b4d8d2acf3229eb2b3f841cfa0 Mon Sep 17 00:00:00 2001 From: SongFerngWang Date: Mon, 4 Nov 2019 23:51:34 +0800 Subject: [PATCH 002/188] [mainline] Expose getTetherApnRequired API as system API 1. getTetherApnRequired API rename to isTetherApnRequired. 2. Expose isTetherApnRequired API as system API. Bug: 142365448 Test: Build pass. atest TetheringConfigurationTest (PASS) Change-Id: Iafb2e4fbddb0251e23673742d990ad098a8ae375 --- .../connectivity/tethering/TetheringConfiguration.java | 2 +- .../tethering/TetheringConfigurationTest.java | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java b/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java index 77097271c4..cf0e3b2ddd 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java +++ b/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java @@ -251,7 +251,7 @@ public class TetheringConfiguration { /** Check whether dun is required. */ public static boolean checkDunRequired(Context ctx, int id) { final TelephonyManager tm = (TelephonyManager) ctx.getSystemService(TELEPHONY_SERVICE); - return (tm != null) ? tm.getTetherApnRequired(id) : false; + return (tm != null) ? tm.isTetherApnRequired(id) : false; } private static Collection getUpstreamIfaceTypes(Resources res, boolean dunRequired) { diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java index 9f9221f301..9c65c0deed 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java +++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java @@ -145,7 +145,7 @@ public class TetheringConfigurationTest { @Test public void testDunFromTelephonyManagerMeansDun() { - when(mTelephonyManager.getTetherApnRequired(anyInt())).thenReturn(true); + when(mTelephonyManager.isTetherApnRequired(anyInt())).thenReturn(true); final TetheringConfiguration cfgWifi = getTetheringConfiguration(TYPE_WIFI); final TetheringConfiguration cfgMobileWifiHipri = getTetheringConfiguration( @@ -169,7 +169,7 @@ public class TetheringConfigurationTest { @Test public void testDunNotRequiredFromTelephonyManagerMeansNoDun() { - when(mTelephonyManager.getTetherApnRequired(anyInt())).thenReturn(false); + when(mTelephonyManager.isTetherApnRequired(anyInt())).thenReturn(false); final TetheringConfiguration cfgWifi = getTetheringConfiguration(TYPE_WIFI); final TetheringConfiguration cfgMobileWifiHipri = getTetheringConfiguration( @@ -212,7 +212,7 @@ public class TetheringConfigurationTest { @Test public void testNoDefinedUpstreamTypesAddsEthernet() { when(mResources.getIntArray(config_tether_upstream_types)).thenReturn(new int[]{}); - when(mTelephonyManager.getTetherApnRequired(anyInt())).thenReturn(false); + when(mTelephonyManager.isTetherApnRequired(anyInt())).thenReturn(false); final TetheringConfiguration cfg = new TetheringConfiguration( mMockContext, mLog, INVALID_SUBSCRIPTION_ID); @@ -235,7 +235,7 @@ public class TetheringConfigurationTest { public void testDefinedUpstreamTypesSansEthernetAddsEthernet() { when(mResources.getIntArray(config_tether_upstream_types)).thenReturn( new int[]{TYPE_WIFI, TYPE_MOBILE_HIPRI}); - when(mTelephonyManager.getTetherApnRequired(anyInt())).thenReturn(false); + when(mTelephonyManager.isTetherApnRequired(anyInt())).thenReturn(false); final TetheringConfiguration cfg = new TetheringConfiguration( mMockContext, mLog, INVALID_SUBSCRIPTION_ID); @@ -253,7 +253,7 @@ public class TetheringConfigurationTest { public void testDefinedUpstreamTypesWithEthernetDoesNotAddEthernet() { when(mResources.getIntArray(config_tether_upstream_types)) .thenReturn(new int[]{TYPE_WIFI, TYPE_ETHERNET, TYPE_MOBILE_HIPRI}); - when(mTelephonyManager.getTetherApnRequired(anyInt())).thenReturn(false); + when(mTelephonyManager.isTetherApnRequired(anyInt())).thenReturn(false); final TetheringConfiguration cfg = new TetheringConfiguration( mMockContext, mLog, INVALID_SUBSCRIPTION_ID); From 42aa1754e2d95145baab895f1a1f6efc4274a647 Mon Sep 17 00:00:00 2001 From: markchien Date: Wed, 23 Oct 2019 16:27:52 +0800 Subject: [PATCH 003/188] [Tether07] Clean up build rule for libtetheroffloadjni - Statically include android.hardware.tetheroffload.config@1.0, libcutils and their dependency library. - Use shared-lib if there is NDK or NDK-compliant version - Remove android.hardware.tetheroffload.control-V1.0-java from service.core because it is already statically built in tethering apk Bug: 143195885 Test: -build, flash, boot Change-Id: Ic082045bc04d3989f7f095c7a499bc0d943e4031 --- Tethering/Android.bp | 21 +++++++++++++---- Tethering/CleanSpec.mk | 52 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 4 deletions(-) create mode 100644 Tethering/CleanSpec.mk diff --git a/Tethering/Android.bp b/Tethering/Android.bp index 7b35f4d56a..cd51f5f90c 100644 --- a/Tethering/Android.bp +++ b/Tethering/Android.bp @@ -41,20 +41,26 @@ android_library { defaults: ["TetheringAndroidLibraryDefaults"], } -cc_library_shared { +// Due to b/143733063, APK can't access a jni lib that is in APEX (but not in the APK). +cc_library { name: "libtetheroffloadjni", srcs: [ "jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp", ], shared_libs: [ - "libnativehelper", - "libcutils", - "android.hardware.tetheroffload.config@1.0", + "libcgrouprc", + "libnativehelper_compat_libc++", + "libvndksupport", ], static_libs: [ + "android.hardware.tetheroffload.config@1.0", "liblog", "libbase", + "libbinderthreadstate", + "libcutils", "libhidlbase", + "libjsoncpp", + "libprocessgroup", "libutils", ], @@ -64,6 +70,8 @@ cc_library_shared { "-Wno-unused-parameter", "-Wthread-safety", ], + + ldflags: ["-Wl,--exclude-libs=ALL,-error-limit=0"], } // Common defaults for compiling the actual APK. @@ -71,7 +79,12 @@ java_defaults { name: "TetheringAppDefaults", platform_apis: true, privileged: true, + // Build system doesn't track transitive dependeicies for jni_libs, list all the dependencies + // explicitly. jni_libs: [ + "libcgrouprc", + "libnativehelper_compat_libc++", + "libvndksupport", "libtetheroffloadjni", ], resource_dirs: [ diff --git a/Tethering/CleanSpec.mk b/Tethering/CleanSpec.mk new file mode 100644 index 0000000000..70db351a69 --- /dev/null +++ b/Tethering/CleanSpec.mk @@ -0,0 +1,52 @@ +# 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. +# + +# If you don't need to do a full clean build but would like to touch +# a file or delete some intermediate files, add a clean step to the end +# of the list. These steps will only be run once, if they haven't been +# run before. +# +# E.g.: +# $(call add-clean-step, touch -c external/sqlite/sqlite3.h) +# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates) +# +# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with +# files that are missing or have been moved. +# +# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory. +# Use $(OUT_DIR) to refer to the "out" directory. +# +# If you need to re-do something that's already mentioned, just copy +# the command and add it to the bottom of the list. E.g., if a change +# that you made last week required touching a file and a change you +# made today requires touching the same file, just copy the old +# touch step and add it to the end of the list. +# +# ***************************************************************** +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THE BANNER +# ***************************************************************** + +# For example: +#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates) +#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates) +#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f) +#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*) + +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/Tethering) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/InProcessTethering) + +# ****************************************************************** +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER +# ****************************************************************** From 2c4cc6f5145006bc315a52e51885a290e2304aaa Mon Sep 17 00:00:00 2001 From: markchien Date: Mon, 30 Sep 2019 14:40:57 +0800 Subject: [PATCH 004/188] [Tether07] Migrate Tethering into module Now tethering would be run in dedicated service. TetheringManager is the interface used to communicate with TetheringService. The new call flow would be: ConnectivityManager -> ConnectivityService -> TetheringManager -> TetheringService. Note: the return value of #tether(), #untether() and #setUsbTethering() APIs would always be no error. Client can use #getLastTetherError() or #getTetheredIfaces or listen tether state change to check status of corresponding interface. Bug: 136040414 Bug: 144742179 Test: -build, flash, boot -atest TetheringTests -atest FrameworksNetTests Change-Id: I7e78c0e0a3e70f940a749ba2a39ece7c7ec5b9b3 --- Tethering/Android.bp | 49 +- Tethering/AndroidManifest.xml | 6 + Tethering/AndroidManifest_InProcess.xml | 35 + Tethering/common/TetheringLib/Android.bp | 13 +- .../android/net/ITetherInternalCallback.aidl | 34 + .../src/android/net/ITetheringConnector.aidl | 17 + .../src/android/net/TetherStatesParcel.aidl | 31 + .../net/TetheringConfigurationParcel.aidl | 37 + .../src/android/net/TetheringManager.java | 507 ++++ ...ity_tethering_OffloadHardwareInterface.cpp | 14 + .../BaseNetdUnsolicitedEventListener.java | 70 + .../net/util/VersionedBroadcastListener.java | 106 + .../tethering/EntitlementManager.java | 19 +- .../tethering/IPv6TetheringCoordinator.java | 311 +++ .../tethering/OffloadHardwareInterface.java | 2 + .../connectivity/tethering/Tethering.java | 2029 +++++++++++++++++ .../tethering/TetheringConfiguration.java | 29 + .../tethering/TetheringDependencies.java | 151 ++ .../tethering/TetheringInterfaceUtils.java | 90 + .../tethering/TetheringService.java | 178 ++ Tethering/tests/unit/Android.bp | 15 - .../util/VersionedBroadcastListenerTest.java | 131 ++ .../tethering/EntitlementManagerTest.java | 45 +- .../connectivity/tethering/TetheringTest.java | 1319 +++++++++++ 24 files changed, 5158 insertions(+), 80 deletions(-) create mode 100644 Tethering/AndroidManifest_InProcess.xml create mode 100644 Tethering/common/TetheringLib/src/android/net/ITetherInternalCallback.aidl create mode 100644 Tethering/common/TetheringLib/src/android/net/TetherStatesParcel.aidl create mode 100644 Tethering/common/TetheringLib/src/android/net/TetheringConfigurationParcel.aidl create mode 100644 Tethering/common/TetheringLib/src/android/net/TetheringManager.java create mode 100644 Tethering/src/android/net/util/BaseNetdUnsolicitedEventListener.java create mode 100644 Tethering/src/android/net/util/VersionedBroadcastListener.java create mode 100644 Tethering/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java create mode 100644 Tethering/src/com/android/server/connectivity/tethering/Tethering.java create mode 100644 Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java create mode 100644 Tethering/src/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java create mode 100644 Tethering/src/com/android/server/connectivity/tethering/TetheringService.java create mode 100644 Tethering/tests/unit/src/android/net/util/VersionedBroadcastListenerTest.java create mode 100644 Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java diff --git a/Tethering/Android.bp b/Tethering/Android.bp index cd51f5f90c..7e8721d3cc 100644 --- a/Tethering/Android.bp +++ b/Tethering/Android.bp @@ -22,13 +22,12 @@ java_defaults { ":framework-tethering-shared-srcs", ":net-module-utils-srcs", ":services-tethering-shared-srcs", - ":servicescore-tethering-src", ], static_libs: [ "androidx.annotation_annotation", - "netd_aidl_interface-java", + "netd_aidl_interface-unstable-java", "netlink-client", - "networkstack-aidl-interfaces-java", + "networkstack-aidl-interfaces-unstable-java", "android.hardware.tetheroffload.control-V1.0-java", "tethering-client", ], @@ -96,7 +95,16 @@ java_defaults { } // Non-updatable tethering running in the system server process for devices not using the module -// TODO: build in-process tethering APK here. +android_app { + name: "InProcessTethering", + defaults: ["TetheringAppDefaults"], + static_libs: ["TetheringApiCurrentLib"], + certificate: "platform", + manifest: "AndroidManifest_InProcess.xml", + // InProcessTethering is a replacement for Tethering + overrides: ["Tethering"], + // TODO: use PlatformNetworkPermissionConfig. +} // Updatable tethering packaged as an application android_app { @@ -109,36 +117,3 @@ android_app { // The permission configuration *must* be included to ensure security of the device required: ["NetworkPermissionConfig"], } - -// This group will be removed when tethering migration is done. -filegroup { - name: "tethering-servicescore-srcs", - srcs: [ - "src/com/android/server/connectivity/tethering/EntitlementManager.java", - "src/com/android/server/connectivity/tethering/OffloadController.java", - "src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java", - "src/com/android/server/connectivity/tethering/TetheringConfiguration.java", - "src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java", - ], -} - -// This group will be removed when tethering migration is done. -filegroup { - name: "tethering-servicesnet-srcs", - srcs: [ - "src/android/net/dhcp/DhcpServerCallbacks.java", - "src/android/net/dhcp/DhcpServingParamsParcelExt.java", - "src/android/net/ip/IpServer.java", - "src/android/net/ip/RouterAdvertisementDaemon.java", - "src/android/net/util/InterfaceSet.java", - "src/android/net/util/PrefixUtils.java", - ], -} - -// This group would be removed when tethering migration is done. -filegroup { - name: "tethering-jni-srcs", - srcs: [ - "jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp", - ], -} diff --git a/Tethering/AndroidManifest.xml b/Tethering/AndroidManifest.xml index eb51593e45..1430ed00aa 100644 --- a/Tethering/AndroidManifest.xml +++ b/Tethering/AndroidManifest.xml @@ -25,5 +25,11 @@ android:process="com.android.networkstack.process" android:extractNativeLibs="false" android:persistent="true"> + + + + + diff --git a/Tethering/AndroidManifest_InProcess.xml b/Tethering/AndroidManifest_InProcess.xml new file mode 100644 index 0000000000..28d405c313 --- /dev/null +++ b/Tethering/AndroidManifest_InProcess.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + diff --git a/Tethering/common/TetheringLib/Android.bp b/Tethering/common/TetheringLib/Android.bp index 5b01b1e038..adc5a723a9 100644 --- a/Tethering/common/TetheringLib/Android.bp +++ b/Tethering/common/TetheringLib/Android.bp @@ -18,8 +18,12 @@ aidl_interface { name: "tethering-aidl-interfaces", local_include_dir: "src", + include_dirs: ["frameworks/base/core/java"], // For framework parcelables. srcs: [ + "src/android/net/ITetherInternalCallback.aidl", "src/android/net/ITetheringConnector.aidl", + "src/android/net/TetheringConfigurationParcel.aidl", + "src/android/net/TetherStatesParcel.aidl", ], backend: { ndk: { @@ -33,8 +37,15 @@ aidl_interface { java_library { name: "tethering-client", - platform_apis: true, + sdk_version: "system_current", static_libs: [ "tethering-aidl-interfaces-java", ], } + +// This is temporary file group which would be removed after TetheringManager is built +// into tethering-client. Will be done by aosp/1156906. +filegroup { + name: "tethering-manager", + srcs: ["src/android/net/TetheringManager.java"], +} diff --git a/Tethering/common/TetheringLib/src/android/net/ITetherInternalCallback.aidl b/Tethering/common/TetheringLib/src/android/net/ITetherInternalCallback.aidl new file mode 100644 index 0000000000..abb00e819f --- /dev/null +++ b/Tethering/common/TetheringLib/src/android/net/ITetherInternalCallback.aidl @@ -0,0 +1,34 @@ +/* + * 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.Network; +import android.net.TetheringConfigurationParcel; +import android.net.TetherStatesParcel; + +/** + * Callback class for receiving tethering changed events + * @hide + */ +oneway interface ITetherInternalCallback +{ + void onUpstreamChanged(in Network network); + void onConfigurationChanged(in TetheringConfigurationParcel config); + void onTetherStatesChanged(in TetherStatesParcel states); + void onCallbackCreated(in Network network, in TetheringConfigurationParcel config, + in TetherStatesParcel states); +} diff --git a/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl b/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl index 443481e33f..bfe502fbeb 100644 --- a/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl +++ b/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl @@ -15,6 +15,23 @@ */ package android.net; +import android.net.ITetherInternalCallback; +import android.os.ResultReceiver; + /** @hide */ oneway interface ITetheringConnector { + void tether(String iface); + + void untether(String iface); + + void setUsbTethering(boolean enable); + + void startTethering(int type, in ResultReceiver receiver, boolean showProvisioningUi); + + void stopTethering(int type); + + void requestLatestTetheringEntitlementResult(int type, in ResultReceiver receiver, + boolean showEntitlementUi); + + void registerTetherInternalCallback(ITetherInternalCallback callback); } diff --git a/Tethering/common/TetheringLib/src/android/net/TetherStatesParcel.aidl b/Tethering/common/TetheringLib/src/android/net/TetherStatesParcel.aidl new file mode 100644 index 0000000000..3d842b3374 --- /dev/null +++ b/Tethering/common/TetheringLib/src/android/net/TetherStatesParcel.aidl @@ -0,0 +1,31 @@ +/* + * 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; + +/** + * Status details for tethering downstream interfaces. + * {@hide} + */ +parcelable TetherStatesParcel { + String[] availableList; + String[] tetheredList; + String[] localOnlyList; + String[] 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/TetheringConfigurationParcel.aidl b/Tethering/common/TetheringLib/src/android/net/TetheringConfigurationParcel.aidl new file mode 100644 index 0000000000..fdf0d16057 --- /dev/null +++ b/Tethering/common/TetheringLib/src/android/net/TetheringConfigurationParcel.aidl @@ -0,0 +1,37 @@ +/* + * 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; + +/** + * Configuration details for a tethering. + * @hide + */ +parcelable TetheringConfigurationParcel { + int subId; + String[] tetherableUsbRegexs; + String[] tetherableWifiRegexs; + String[] tetherableBluetoothRegexs; + boolean isDunRequired; + boolean chooseUpstreamAutomatically; + int[] preferredUpstreamIfaceTypes; + String[] legacyDhcpRanges; + String[] defaultIPv4DNS; + boolean enableLegacyDhcpServer; + String[] provisioningApp; + String provisioningAppNoUi; + int provisioningCheckPeriod; +} diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java new file mode 100644 index 0000000000..9e8726edf0 --- /dev/null +++ b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java @@ -0,0 +1,507 @@ +/* + * 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 static android.Manifest.permission.NETWORK_STACK; +import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.util.SharedLog; +import android.os.ConditionVariable; +import android.os.IBinder; +import android.os.RemoteCallbackList; +import android.os.RemoteException; +import android.os.ResultReceiver; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; + +import java.io.PrintWriter; +import java.util.StringJoiner; + +/** + * Service used to communicate with the tethering, which is running in a separate module. + * @hide + */ +public class TetheringManager { + private static final String TAG = TetheringManager.class.getSimpleName(); + + private static TetheringManager sInstance; + + @Nullable + private ITetheringConnector mConnector; + private TetherInternalCallback mCallback; + private Network mTetherUpstream; + private TetheringConfigurationParcel mTetheringConfiguration; + private TetherStatesParcel mTetherStatesParcel; + + private final RemoteCallbackList mTetheringEventCallbacks = + new RemoteCallbackList<>(); + @GuardedBy("mLog") + private final SharedLog mLog = new SharedLog(TAG); + + private TetheringManager() { } + + /** + * Get the TetheringManager singleton instance. + */ + public static synchronized TetheringManager getInstance() { + if (sInstance == null) { + sInstance = new TetheringManager(); + } + return sInstance; + } + + private class TetheringConnection implements + ConnectivityModuleConnector.ModuleServiceCallback { + @Override + public void onModuleServiceConnected(@NonNull IBinder service) { + logi("Tethering service connected"); + registerTetheringService(service); + } + } + + private void registerTetheringService(@NonNull IBinder service) { + final ITetheringConnector connector = ITetheringConnector.Stub.asInterface(service); + + log("Tethering service registered"); + + // Currently TetheringManager instance is only used by ConnectivityService and mConnector + // only expect to assign once when system server start and bind tethering service. + // STOPSHIP: Change mConnector to final before TetheringManager put into boot classpath. + mConnector = connector; + mCallback = new TetherInternalCallback(); + try { + mConnector.registerTetherInternalCallback(mCallback); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + private class TetherInternalCallback extends ITetherInternalCallback.Stub { + private final ConditionVariable mWaitForCallback = new ConditionVariable(false); + private static final int EVENT_CALLBACK_TIMEOUT_MS = 60_000; + + @Override + public void onUpstreamChanged(Network network) { + mTetherUpstream = network; + reportUpstreamChanged(network); + } + + @Override + public void onConfigurationChanged(TetheringConfigurationParcel config) { + mTetheringConfiguration = config; + } + + @Override + public void onTetherStatesChanged(TetherStatesParcel states) { + mTetherStatesParcel = states; + } + + @Override + public void onCallbackCreated(Network network, TetheringConfigurationParcel config, + TetherStatesParcel states) { + mTetherUpstream = network; + mTetheringConfiguration = config; + mTetherStatesParcel = states; + mWaitForCallback.open(); + } + + boolean awaitCallbackCreation() { + return mWaitForCallback.block(EVENT_CALLBACK_TIMEOUT_MS); + } + } + + private void reportUpstreamChanged(Network network) { + final int length = mTetheringEventCallbacks.beginBroadcast(); + try { + for (int i = 0; i < length; i++) { + try { + mTetheringEventCallbacks.getBroadcastItem(i).onUpstreamChanged(network); + } catch (RemoteException e) { + // Not really very much to do here. + } + } + } finally { + mTetheringEventCallbacks.finishBroadcast(); + } + } + + /** + * Start the tethering service. Should be called only once on device startup. + * + *

This method will start the tethering service either in the network stack process, + * or inside the system server on devices that do not support the tethering module. + * + * {@hide} + */ + public void start() { + // Using MAINLINE_NETWORK_STACK permission after cutting off the dpendency of system server. + ConnectivityModuleConnector.getInstance().startModuleService( + ITetheringConnector.class.getName(), NETWORK_STACK, + new TetheringConnection()); + log("Tethering service start requested"); + } + + /** + * Attempt to tether the named interface. This will setup a dhcp server + * on the interface, forward and NAT IP v4 packets and forward DNS requests + * to the best active upstream network interface. Note that if no upstream + * IP network interface is available, dhcp will still run and traffic will be + * allowed between the tethered devices and this device, though upstream net + * access will of course fail until an upstream network interface becomes + * active. Note: return value do not have any meaning. It is better to use + * #getTetherableIfaces() to ensure corresponding interface is available for + * tethering before calling #tether(). + * + * TODO: Deprecate this API. The only usages should be in PanService and Wifi P2P which + * need direct access. + * + * {@hide} + */ + public int tether(@NonNull String iface) { + try { + mConnector.tether(iface); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + return TETHER_ERROR_NO_ERROR; + } + + /** + * Stop tethering the named interface. + * + * {@hide} + */ + public int untether(@NonNull String iface) { + try { + mConnector.untether(iface); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + return TETHER_ERROR_NO_ERROR; + } + + /** + * Attempt to both alter the mode of USB and Tethering of USB. WARNING: New client 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. + * + * @Deprecated + * {@hide} + */ + public int setUsbTethering(boolean enable) { + try { + mConnector.setUsbTethering(enable); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + return TETHER_ERROR_NO_ERROR; + } + + /** + * Starts tethering and runs tether provisioning for the given type if needed. If provisioning + * fails, stopTethering will be called automatically. + * + * {@hide} + */ + // TODO: improve the usage of ResultReceiver, b/145096122 + public void startTethering(int type, @NonNull ResultReceiver receiver, + boolean showProvisioningUi) { + try { + mConnector.startTethering(type, receiver, showProvisioningUi); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** + * Stops tethering for the given type. Also cancels any provisioning rechecks for that type if + * applicable. + * + * {@hide} + */ + public void stopTethering(int type) { + try { + mConnector.stopTethering(type); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** + * Request the latest value of the tethering entitlement check. + * + * Note: Allow privileged apps who have TETHER_PRIVILEGED permission to access. If it turns + * out some such apps are observed to abuse this API, change to per-UID limits on this API + * if it's really needed. + */ + // TODO: improve the usage of ResultReceiver, b/145096122 + public void requestLatestTetheringEntitlementResult(int type, @NonNull ResultReceiver receiver, + boolean showEntitlementUi) { + try { + mConnector.requestLatestTetheringEntitlementResult(type, receiver, showEntitlementUi); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** + * Register tethering event callback. + * + * {@hide} + */ + public void registerTetheringEventCallback(@NonNull ITetheringEventCallback callback) { + mTetheringEventCallbacks.register(callback); + } + + /** + * Unregister tethering event callback. + * + * {@hide} + */ + public void unregisterTetheringEventCallback(@NonNull ITetheringEventCallback callback) { + mTetheringEventCallbacks.unregister(callback); + } + + /** + * Get a more detailed error code after a Tethering or Untethering + * request asynchronously failed. + * + * {@hide} + */ + public int getLastTetherError(@NonNull String iface) { + if (!mCallback.awaitCallbackCreation()) { + throw new NullPointerException("callback was not ready yet"); + } + if (mTetherStatesParcel == null) return TETHER_ERROR_NO_ERROR; + + int i = 0; + for (String errored : mTetherStatesParcel.erroredIfaceList) { + if (iface.equals(errored)) return mTetherStatesParcel.lastErrorList[i]; + + i++; + } + return TETHER_ERROR_NO_ERROR; + } + + /** + * Get the list of regular expressions that define any tetherable + * USB network interfaces. If USB tethering is not supported by the + * device, this list should be empty. + * + * {@hide} + */ + public @NonNull String[] getTetherableUsbRegexs() { + if (!mCallback.awaitCallbackCreation()) { + throw new NullPointerException("callback was not ready yet"); + } + return mTetheringConfiguration.tetherableUsbRegexs; + } + + /** + * Get the list of regular expressions that define any tetherable + * Wifi network interfaces. If Wifi tethering is not supported by the + * device, this list should be empty. + * + * {@hide} + */ + public @NonNull String[] getTetherableWifiRegexs() { + if (!mCallback.awaitCallbackCreation()) { + throw new NullPointerException("callback was not ready yet"); + } + return mTetheringConfiguration.tetherableWifiRegexs; + } + + /** + * Get the list of regular expressions that define any tetherable + * Bluetooth network interfaces. If Bluetooth tethering is not supported by the + * device, this list should be empty. + * + * {@hide} + */ + public @NonNull String[] getTetherableBluetoothRegexs() { + if (!mCallback.awaitCallbackCreation()) { + throw new NullPointerException("callback was not ready yet"); + } + return mTetheringConfiguration.tetherableBluetoothRegexs; + } + + /** + * Get the set of tetherable, available interfaces. This list is limited by + * device configuration and current interface existence. + * + * {@hide} + */ + public @NonNull String[] getTetherableIfaces() { + if (!mCallback.awaitCallbackCreation()) { + throw new NullPointerException("callback was not ready yet"); + } + if (mTetherStatesParcel == null) return new String[0]; + return mTetherStatesParcel.availableList; + } + + /** + * Get the set of tethered interfaces. + * + * {@hide} + */ + public @NonNull String[] getTetheredIfaces() { + if (!mCallback.awaitCallbackCreation()) { + throw new NullPointerException("callback was not ready yet"); + } + if (mTetherStatesParcel == null) return new String[0]; + return mTetherStatesParcel.tetheredList; + } + + /** + * Get the set of interface names which attempted to tether but + * failed. + * + * {@hide} + */ + public @NonNull String[] getTetheringErroredIfaces() { + if (!mCallback.awaitCallbackCreation()) { + throw new NullPointerException("callback was not ready yet"); + } + if (mTetherStatesParcel == null) return new String[0]; + return mTetherStatesParcel.erroredIfaceList; + } + + /** + * Get the set of tethered dhcp ranges. + * + * {@hide} + */ + public @NonNull String[] getTetheredDhcpRanges() { + if (!mCallback.awaitCallbackCreation()) { + throw new NullPointerException("callback was not ready yet"); + } + return mTetheringConfiguration.legacyDhcpRanges; + } + + /** + * Check if the device allows for tethering. + * + * {@hide} + */ + public boolean hasTetherableConfiguration() { + if (!mCallback.awaitCallbackCreation()) { + throw new NullPointerException("callback was not ready yet"); + } + final boolean hasDownstreamConfiguration = + (mTetheringConfiguration.tetherableUsbRegexs.length != 0) + || (mTetheringConfiguration.tetherableWifiRegexs.length != 0) + || (mTetheringConfiguration.tetherableBluetoothRegexs.length != 0); + final boolean hasUpstreamConfiguration = + (mTetheringConfiguration.preferredUpstreamIfaceTypes.length != 0) + || mTetheringConfiguration.chooseUpstreamAutomatically; + + return hasDownstreamConfiguration && hasUpstreamConfiguration; + } + + /** + * Log a message in the local log. + */ + private void log(@NonNull String message) { + synchronized (mLog) { + mLog.log(message); + } + } + + /** + * Log a condition that should never happen. + */ + private void logWtf(@NonNull String message, @Nullable Throwable e) { + Slog.wtf(TAG, message); + synchronized (mLog) { + mLog.e(message, e); + } + } + + /** + * Log a ERROR level message in the local and system logs. + */ + private void loge(@NonNull String message, @Nullable Throwable e) { + synchronized (mLog) { + mLog.e(message, e); + } + } + + /** + * Log a INFO level message in the local and system logs. + */ + private void logi(@NonNull String message) { + synchronized (mLog) { + mLog.i(message); + } + } + + /** + * Dump TetheringManager logs to the specified {@link PrintWriter}. + */ + public void dump(@NonNull PrintWriter pw) { + // dump is thread-safe on SharedLog + mLog.dump(null, pw, null); + + pw.print("subId: "); + pw.println(mTetheringConfiguration.subId); + + dumpStringArray(pw, "tetherableUsbRegexs", + mTetheringConfiguration.tetherableUsbRegexs); + dumpStringArray(pw, "tetherableWifiRegexs", + mTetheringConfiguration.tetherableWifiRegexs); + dumpStringArray(pw, "tetherableBluetoothRegexs", + mTetheringConfiguration.tetherableBluetoothRegexs); + + pw.print("isDunRequired: "); + pw.println(mTetheringConfiguration.isDunRequired); + + pw.print("chooseUpstreamAutomatically: "); + pw.println(mTetheringConfiguration.chooseUpstreamAutomatically); + + dumpStringArray(pw, "legacyDhcpRanges", mTetheringConfiguration.legacyDhcpRanges); + dumpStringArray(pw, "defaultIPv4DNS", mTetheringConfiguration.defaultIPv4DNS); + + dumpStringArray(pw, "provisioningApp", mTetheringConfiguration.provisioningApp); + pw.print("provisioningAppNoUi: "); + pw.println(mTetheringConfiguration.provisioningAppNoUi); + + pw.print("enableLegacyDhcpServer: "); + pw.println(mTetheringConfiguration.enableLegacyDhcpServer); + + pw.println(); + } + + private static void dumpStringArray(@NonNull PrintWriter pw, @NonNull String label, + @Nullable String[] values) { + pw.print(label); + pw.print(": "); + + if (values != null) { + final StringJoiner sj = new StringJoiner(", ", "[", "]"); + for (String value : values) sj.add(value); + + pw.print(sj.toString()); + } else { + pw.print("null"); + } + + pw.println(); + } +} diff --git a/Tethering/jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp b/Tethering/jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp index 3eaf48845a..663154a490 100644 --- a/Tethering/jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp +++ b/Tethering/jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp @@ -145,4 +145,18 @@ int register_android_server_connectivity_tethering_OffloadHardwareInterface(JNIE gMethods, NELEM(gMethods)); } +extern "C" jint JNI_OnLoad(JavaVM* vm, void*) { + JNIEnv *env; + if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { + ALOGE("ERROR: GetEnv failed"); + return JNI_ERR; + } + + if (register_android_server_connectivity_tethering_OffloadHardwareInterface(env) < 0) { + return JNI_ERR; + } + + return JNI_VERSION_1_6; +} + }; // namespace android diff --git a/Tethering/src/android/net/util/BaseNetdUnsolicitedEventListener.java b/Tethering/src/android/net/util/BaseNetdUnsolicitedEventListener.java new file mode 100644 index 0000000000..3218c0b387 --- /dev/null +++ b/Tethering/src/android/net/util/BaseNetdUnsolicitedEventListener.java @@ -0,0 +1,70 @@ +/* + * 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.util; + +import android.net.INetdUnsolicitedEventListener; + +import androidx.annotation.NonNull; + +/** + * Base {@link INetdUnsolicitedEventListener} that provides no-op implementations which can be + * overridden. + */ +public class BaseNetdUnsolicitedEventListener extends INetdUnsolicitedEventListener.Stub { + + @Override + public void onInterfaceClassActivityChanged(boolean isActive, int timerLabel, long timestampNs, + int uid) { } + + @Override + public void onQuotaLimitReached(@NonNull String alertName, @NonNull String ifName) { } + + @Override + public void onInterfaceDnsServerInfo(@NonNull String ifName, long lifetimeS, + @NonNull String[] servers) { } + + @Override + public void onInterfaceAddressUpdated(@NonNull String addr, String ifName, int flags, + int scope) { } + + @Override + public void onInterfaceAddressRemoved(@NonNull String addr, @NonNull String ifName, int flags, + int scope) { } + + @Override + public void onInterfaceAdded(@NonNull String ifName) { } + + @Override + public void onInterfaceRemoved(@NonNull String ifName) { } + + @Override + public void onInterfaceChanged(@NonNull String ifName, boolean up) { } + + @Override + public void onInterfaceLinkStateChanged(@NonNull String ifName, boolean up) { } + + @Override + public void onRouteChanged(boolean updated, @NonNull String route, @NonNull String gateway, + @NonNull String ifName) { } + + @Override + public void onStrictCleartextDetected(int uid, @NonNull String hex) { } + + @Override + public int getInterfaceVersion() { + return INetdUnsolicitedEventListener.VERSION; + } +} diff --git a/Tethering/src/android/net/util/VersionedBroadcastListener.java b/Tethering/src/android/net/util/VersionedBroadcastListener.java new file mode 100644 index 0000000000..e2804abd75 --- /dev/null +++ b/Tethering/src/android/net/util/VersionedBroadcastListener.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2017 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.util; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Handler; +import android.util.Log; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; + + +/** + * A utility class that runs the provided callback on the provided handler when + * intents matching the provided filter arrive. Intents received by a stale + * receiver are safely ignored. + * + * Calls to startListening() and stopListening() must happen on the same thread. + * + * @hide + */ +public class VersionedBroadcastListener { + private static final boolean DBG = false; + + private final String mTag; + private final Context mContext; + private final Handler mHandler; + private final IntentFilter mFilter; + private final Consumer mCallback; + private final AtomicInteger mGenerationNumber; + private BroadcastReceiver mReceiver; + + public VersionedBroadcastListener(String tag, Context ctx, Handler handler, + IntentFilter filter, Consumer callback) { + mTag = tag; + mContext = ctx; + mHandler = handler; + mFilter = filter; + mCallback = callback; + mGenerationNumber = new AtomicInteger(0); + } + + /** Start listening to intent broadcast. */ + public void startListening() { + if (DBG) Log.d(mTag, "startListening"); + if (mReceiver != null) return; + + mReceiver = new Receiver(mTag, mGenerationNumber, mCallback); + mContext.registerReceiver(mReceiver, mFilter, null, mHandler); + } + + /** Stop listening to intent broadcast. */ + public void stopListening() { + if (DBG) Log.d(mTag, "stopListening"); + if (mReceiver == null) return; + + mGenerationNumber.incrementAndGet(); + mContext.unregisterReceiver(mReceiver); + mReceiver = null; + } + + private static class Receiver extends BroadcastReceiver { + public final String tag; + public final AtomicInteger atomicGenerationNumber; + public final Consumer callback; + // Used to verify this receiver is still current. + public final int generationNumber; + + Receiver(String tag, AtomicInteger atomicGenerationNumber, Consumer callback) { + this.tag = tag; + this.atomicGenerationNumber = atomicGenerationNumber; + this.callback = callback; + generationNumber = atomicGenerationNumber.incrementAndGet(); + } + + @Override + public void onReceive(Context context, Intent intent) { + final int currentGenerationNumber = atomicGenerationNumber.get(); + + if (DBG) { + Log.d(tag, "receiver generationNumber=" + generationNumber + + ", current generationNumber=" + currentGenerationNumber); + } + if (generationNumber != currentGenerationNumber) return; + + callback.accept(intent); + } + } +} diff --git a/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java b/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java index 6b0f1de7ce..ba5d08dce4 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java +++ b/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java @@ -47,6 +47,7 @@ import android.os.Parcel; import android.os.PersistableBundle; import android.os.ResultReceiver; import android.os.SystemClock; +import android.os.SystemProperties; import android.os.UserHandle; import android.provider.Settings; import android.telephony.CarrierConfigManager; @@ -55,7 +56,6 @@ import android.util.SparseIntArray; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.StateMachine; -import com.android.server.connectivity.MockableSystemProperties; import java.io.PrintWriter; @@ -94,7 +94,6 @@ public class EntitlementManager { private final ArraySet mCurrentTethers; private final Context mContext; private final int mPermissionChangeMessageCode; - private final MockableSystemProperties mSystemProperties; private final SharedLog mLog; private final SparseIntArray mEntitlementCacheValue; private final EntitlementHandler mHandler; @@ -110,12 +109,12 @@ public class EntitlementManager { private TetheringConfigurationFetcher mFetcher; public EntitlementManager(Context ctx, StateMachine tetherMasterSM, SharedLog log, - int permissionChangeMessageCode, MockableSystemProperties systemProperties) { + int permissionChangeMessageCode) { + mContext = ctx; mLog = log.forSubComponent(TAG); mCurrentTethers = new ArraySet(); mCellularPermitted = new SparseIntArray(); - mSystemProperties = systemProperties; mEntitlementCacheValue = new SparseIntArray(); mTetherMasterSM = tetherMasterSM; mPermissionChangeMessageCode = permissionChangeMessageCode; @@ -287,7 +286,7 @@ public class EntitlementManager { */ @VisibleForTesting protected boolean isTetherProvisioningRequired(final TetheringConfiguration config) { - if (mSystemProperties.getBoolean(DISABLE_PROVISIONING_SYSPROP_KEY, false) + if (SystemProperties.getBoolean(DISABLE_PROVISIONING_SYSPROP_KEY, false) || config.provisioningApp.length == 0) { return false; } @@ -526,8 +525,8 @@ public class EntitlementManager { handleMaybeRunProvisioning(config); break; case EVENT_GET_ENTITLEMENT_VALUE: - handleGetLatestTetheringEntitlementValue(msg.arg1, (ResultReceiver) msg.obj, - toBool(msg.arg2)); + handleRequestLatestTetheringEntitlementValue(msg.arg1, + (ResultReceiver) msg.obj, toBool(msg.arg2)); break; default: mLog.log("Unknown event: " + msg.what); @@ -651,15 +650,15 @@ public class EntitlementManager { } /** Get the last value of the tethering entitlement check. */ - public void getLatestTetheringEntitlementResult(int downstream, ResultReceiver receiver, + public void requestLatestTetheringEntitlementResult(int downstream, ResultReceiver receiver, boolean showEntitlementUi) { mHandler.sendMessage(mHandler.obtainMessage(EVENT_GET_ENTITLEMENT_VALUE, downstream, encodeBool(showEntitlementUi), receiver)); } - private void handleGetLatestTetheringEntitlementValue(int downstream, ResultReceiver receiver, - boolean showEntitlementUi) { + private void handleRequestLatestTetheringEntitlementValue(int downstream, + ResultReceiver receiver, boolean showEntitlementUi) { final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); if (!isTetherProvisioningRequired(config)) { receiver.send(TETHER_ERROR_NO_ERROR, null); diff --git a/Tethering/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java b/Tethering/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java new file mode 100644 index 0000000000..edfe3cad90 --- /dev/null +++ b/Tethering/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.connectivity.tethering; + +import android.net.IpPrefix; +import android.net.LinkAddress; +import android.net.LinkProperties; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.NetworkState; +import android.net.RouteInfo; +import android.net.ip.IpServer; +import android.net.util.NetworkConstants; +import android.net.util.SharedLog; +import android.util.Log; + +import java.net.Inet6Address; +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.Random; + + +/** + * IPv6 tethering is rather different from IPv4 owing to the absence of NAT. + * This coordinator is responsible for evaluating the dedicated prefixes + * assigned to the device and deciding how to divvy them up among downstream + * interfaces. + * + * @hide + */ +public class IPv6TetheringCoordinator { + private static final String TAG = IPv6TetheringCoordinator.class.getSimpleName(); + private static final boolean DBG = false; + private static final boolean VDBG = false; + + private static class Downstream { + public final IpServer ipServer; + public final int mode; // IpServer.STATE_* + // Used to append to a ULA /48, constructing a ULA /64 for local use. + public final short subnetId; + + Downstream(IpServer ipServer, int mode, short subnetId) { + this.ipServer = ipServer; + this.mode = mode; + this.subnetId = subnetId; + } + } + + private final ArrayList mNotifyList; + private final SharedLog mLog; + // NOTE: mActiveDownstreams is a list and not a hash data structure because + // we keep active downstreams in arrival order. This is done so /64s can + // be parceled out on a "first come, first served" basis and a /64 used by + // a downstream that is no longer active can be redistributed to any next + // waiting active downstream (again, in arrival order). + private final LinkedList mActiveDownstreams; + private final byte[] mUniqueLocalPrefix; + private short mNextSubnetId; + private NetworkState mUpstreamNetworkState; + + public IPv6TetheringCoordinator(ArrayList notifyList, SharedLog log) { + mNotifyList = notifyList; + mLog = log.forSubComponent(TAG); + mActiveDownstreams = new LinkedList<>(); + mUniqueLocalPrefix = generateUniqueLocalPrefix(); + mNextSubnetId = 0; + } + + /** Add active downstream to ipv6 tethering candidate list. */ + public void addActiveDownstream(IpServer downstream, int mode) { + if (findDownstream(downstream) == null) { + // Adding a new downstream appends it to the list. Adding a + // downstream a second time without first removing it has no effect. + // We never change the mode of a downstream except by first removing + // it and then re-adding it (with its new mode specified); + if (mActiveDownstreams.offer(new Downstream(downstream, mode, mNextSubnetId))) { + // Make sure subnet IDs are always positive. They are appended + // to a ULA /48 to make a ULA /64 for local use. + mNextSubnetId = (short) Math.max(0, mNextSubnetId + 1); + } + updateIPv6TetheringInterfaces(); + } + } + + /** Remove downstream from ipv6 tethering candidate list. */ + public void removeActiveDownstream(IpServer downstream) { + stopIPv6TetheringOn(downstream); + if (mActiveDownstreams.remove(findDownstream(downstream))) { + updateIPv6TetheringInterfaces(); + } + + // When tethering is stopping we can reset the subnet counter. + if (mNotifyList.isEmpty()) { + if (!mActiveDownstreams.isEmpty()) { + Log.wtf(TAG, "Tethering notify list empty, IPv6 downstreams non-empty."); + } + mNextSubnetId = 0; + } + } + + /** + * Call when upstream NetworkState may be changed. + * If upstream has ipv6 for tethering, update this new NetworkState + * to IpServer. Otherwise stop ipv6 tethering on downstream interfaces. + */ + public void updateUpstreamNetworkState(NetworkState ns) { + if (VDBG) { + Log.d(TAG, "updateUpstreamNetworkState: " + toDebugString(ns)); + } + if (TetheringInterfaceUtils.getIPv6Interface(ns) == null) { + stopIPv6TetheringOnAllInterfaces(); + setUpstreamNetworkState(null); + return; + } + + if (mUpstreamNetworkState != null + && !ns.network.equals(mUpstreamNetworkState.network)) { + stopIPv6TetheringOnAllInterfaces(); + } + + setUpstreamNetworkState(ns); + updateIPv6TetheringInterfaces(); + } + + private void stopIPv6TetheringOnAllInterfaces() { + for (IpServer ipServer : mNotifyList) { + stopIPv6TetheringOn(ipServer); + } + } + + private void setUpstreamNetworkState(NetworkState ns) { + if (ns == null) { + mUpstreamNetworkState = null; + } else { + // Make a deep copy of the parts we need. + mUpstreamNetworkState = new NetworkState( + null, + new LinkProperties(ns.linkProperties), + new NetworkCapabilities(ns.networkCapabilities), + new Network(ns.network), + null, + null); + } + + mLog.log("setUpstreamNetworkState: " + toDebugString(mUpstreamNetworkState)); + } + + private void updateIPv6TetheringInterfaces() { + for (IpServer ipServer : mNotifyList) { + final LinkProperties lp = getInterfaceIPv6LinkProperties(ipServer); + ipServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, lp); + break; + } + } + + private LinkProperties getInterfaceIPv6LinkProperties(IpServer ipServer) { + final Downstream ds = findDownstream(ipServer); + if (ds == null) return null; + + if (ds.mode == IpServer.STATE_LOCAL_ONLY) { + // Build a Unique Locally-assigned Prefix configuration. + return getUniqueLocalConfig(mUniqueLocalPrefix, ds.subnetId); + } + + // This downstream is in IpServer.STATE_TETHERED mode. + if (mUpstreamNetworkState == null || mUpstreamNetworkState.linkProperties == null) { + return null; + } + + // NOTE: Here, in future, we would have policies to decide how to divvy + // up the available dedicated prefixes among downstream interfaces. + // At this time we have no such mechanism--we only support tethering + // IPv6 toward the oldest (first requested) active downstream. + + final Downstream currentActive = mActiveDownstreams.peek(); + if (currentActive != null && currentActive.ipServer == ipServer) { + final LinkProperties lp = getIPv6OnlyLinkProperties( + mUpstreamNetworkState.linkProperties); + if (lp.hasIpv6DefaultRoute() && lp.hasGlobalIpv6Address()) { + return lp; + } + } + + return null; + } + + Downstream findDownstream(IpServer ipServer) { + for (Downstream ds : mActiveDownstreams) { + if (ds.ipServer == ipServer) return ds; + } + return null; + } + + private static LinkProperties getIPv6OnlyLinkProperties(LinkProperties lp) { + final LinkProperties v6only = new LinkProperties(); + if (lp == null) { + return v6only; + } + + // NOTE: At this time we don't copy over any information about any + // stacked links. No current stacked link configuration has IPv6. + + v6only.setInterfaceName(lp.getInterfaceName()); + + v6only.setMtu(lp.getMtu()); + + for (LinkAddress linkAddr : lp.getLinkAddresses()) { + if (linkAddr.isGlobalPreferred() && linkAddr.getPrefixLength() == 64) { + v6only.addLinkAddress(linkAddr); + } + } + + for (RouteInfo routeInfo : lp.getRoutes()) { + final IpPrefix destination = routeInfo.getDestination(); + if ((destination.getAddress() instanceof Inet6Address) + && (destination.getPrefixLength() <= 64)) { + v6only.addRoute(routeInfo); + } + } + + for (InetAddress dnsServer : lp.getDnsServers()) { + if (isIPv6GlobalAddress(dnsServer)) { + // For now we include ULAs. + v6only.addDnsServer(dnsServer); + } + } + + v6only.setDomains(lp.getDomains()); + + return v6only; + } + + // TODO: Delete this and switch to LinkAddress#isGlobalPreferred once we + // announce our own IPv6 address as DNS server. + private static boolean isIPv6GlobalAddress(InetAddress ip) { + return (ip instanceof Inet6Address) + && !ip.isAnyLocalAddress() + && !ip.isLoopbackAddress() + && !ip.isLinkLocalAddress() + && !ip.isSiteLocalAddress() + && !ip.isMulticastAddress(); + } + + private static LinkProperties getUniqueLocalConfig(byte[] ulp, short subnetId) { + final LinkProperties lp = new LinkProperties(); + + final IpPrefix local48 = makeUniqueLocalPrefix(ulp, (short) 0, 48); + lp.addRoute(new RouteInfo(local48, null, null)); + + final IpPrefix local64 = makeUniqueLocalPrefix(ulp, subnetId, 64); + // Because this is a locally-generated ULA, we don't have an upstream + // address. But because the downstream IP address management code gets + // its prefix from the upstream's IP address, we create a fake one here. + lp.addLinkAddress(new LinkAddress(local64.getAddress(), 64)); + + lp.setMtu(NetworkConstants.ETHER_MTU); + return lp; + } + + private static IpPrefix makeUniqueLocalPrefix(byte[] in6addr, short subnetId, int prefixlen) { + final byte[] bytes = Arrays.copyOf(in6addr, in6addr.length); + bytes[7] = (byte) (subnetId >> 8); + bytes[8] = (byte) subnetId; + return new IpPrefix(bytes, prefixlen); + } + + // Generates a Unique Locally-assigned Prefix: + // + // https://tools.ietf.org/html/rfc4193#section-3.1 + // + // The result is a /48 that can be used for local-only communications. + private static byte[] generateUniqueLocalPrefix() { + final byte[] ulp = new byte[6]; // 6 = 48bits / 8bits/byte + (new Random()).nextBytes(ulp); + + final byte[] in6addr = Arrays.copyOf(ulp, NetworkConstants.IPV6_ADDR_LEN); + in6addr[0] = (byte) 0xfd; // fc00::/7 and L=1 + + return in6addr; + } + + private static String toDebugString(NetworkState ns) { + if (ns == null) { + return "NetworkState{null}"; + } + return String.format("NetworkState{%s, %s, %s}", + ns.network, + ns.networkCapabilities, + ns.linkProperties); + } + + private static void stopIPv6TetheringOn(IpServer ipServer) { + ipServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null); + } +} diff --git a/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java b/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java index 01339a4a2c..00a677338b 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java +++ b/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java @@ -107,6 +107,8 @@ public class OffloadHardwareInterface { public OffloadHardwareInterface(Handler h, SharedLog log) { mHandler = h; mLog = log.forSubComponent(TAG); + + System.loadLibrary("tetheroffloadjni"); } /** Get default value indicating whether offload is supported. */ diff --git a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java new file mode 100644 index 0000000000..058fb4fe98 --- /dev/null +++ b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java @@ -0,0 +1,2029 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.connectivity.tethering; + +import static android.hardware.usb.UsbManager.USB_CONFIGURED; +import static android.hardware.usb.UsbManager.USB_CONNECTED; +import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS; +import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED; +import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; +import static android.net.ConnectivityManager.EXTRA_ACTIVE_LOCAL_ONLY; +import static android.net.ConnectivityManager.EXTRA_ACTIVE_TETHER; +import static android.net.ConnectivityManager.EXTRA_AVAILABLE_TETHER; +import static android.net.ConnectivityManager.EXTRA_ERRORED_TETHER; +import static android.net.ConnectivityManager.EXTRA_NETWORK_INFO; +import static android.net.ConnectivityManager.TETHERING_BLUETOOTH; +import static android.net.ConnectivityManager.TETHERING_INVALID; +import static android.net.ConnectivityManager.TETHERING_USB; +import static android.net.ConnectivityManager.TETHERING_WIFI; +import static android.net.ConnectivityManager.TETHERING_WIFI_P2P; +import static android.net.ConnectivityManager.TETHER_ERROR_MASTER_ERROR; +import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR; +import static android.net.ConnectivityManager.TETHER_ERROR_SERVICE_UNAVAIL; +import static android.net.ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE; +import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE; +import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME; +import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE; +import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE; +import static android.net.wifi.WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR; +import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY; +import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED; +import static android.net.wifi.WifiManager.IFACE_IP_MODE_UNSPECIFIED; +import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED; +import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED; +import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothPan; +import android.bluetooth.BluetoothProfile; +import android.bluetooth.BluetoothProfile.ServiceListener; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.res.Resources; +import android.hardware.usb.UsbManager; +import android.net.INetd; +import android.net.INetworkPolicyManager; +import android.net.INetworkStatsService; +import android.net.ITetherInternalCallback; +import android.net.IpPrefix; +import android.net.LinkAddress; +import android.net.LinkProperties; +import android.net.Network; +import android.net.NetworkInfo; +import android.net.NetworkState; +import android.net.NetworkUtils; +import android.net.TetherStatesParcel; +import android.net.TetheringConfigurationParcel; +import android.net.ip.IpServer; +import android.net.util.BaseNetdUnsolicitedEventListener; +import android.net.util.InterfaceSet; +import android.net.util.PrefixUtils; +import android.net.util.SharedLog; +import android.net.util.VersionedBroadcastListener; +import android.net.wifi.WifiManager; +import android.net.wifi.p2p.WifiP2pGroup; +import android.net.wifi.p2p.WifiP2pInfo; +import android.net.wifi.p2p.WifiP2pManager; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.INetworkManagementService; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.os.ResultReceiver; +import android.os.UserHandle; +import android.os.UserManager; +import android.os.UserManagerInternal; +import android.os.UserManagerInternal.UserRestrictionsListener; +import android.telephony.PhoneStateListener; +import android.telephony.TelephonyManager; +import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.Log; +import android.util.SparseArray; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; +import com.android.internal.notification.SystemNotificationChannels; +import com.android.internal.util.DumpUtils; +import com.android.internal.util.IndentingPrintWriter; +import com.android.internal.util.MessageUtils; +import com.android.internal.util.Protocol; +import com.android.internal.util.State; +import com.android.internal.util.StateMachine; +import com.android.server.LocalServices; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +/** + * + * This class holds much of the business logic to allow Android devices + * to act as IP gateways via USB, BT, and WiFi interfaces. + */ +public class Tethering { + + private static final String TAG = Tethering.class.getSimpleName(); + private static final boolean DBG = false; + private static final boolean VDBG = false; + + private static final Class[] sMessageClasses = { + Tethering.class, TetherMasterSM.class, IpServer.class + }; + private static final SparseArray sMagicDecoderRing = + MessageUtils.findMessageNames(sMessageClasses); + + private static class TetherState { + public final IpServer ipServer; + public int lastState; + public int lastError; + + TetherState(IpServer ipServer) { + this.ipServer = ipServer; + // Assume all state machines start out available and with no errors. + lastState = IpServer.STATE_AVAILABLE; + lastError = TETHER_ERROR_NO_ERROR; + } + + public boolean isCurrentlyServing() { + switch (lastState) { + case IpServer.STATE_TETHERED: + case IpServer.STATE_LOCAL_ONLY: + return true; + default: + return false; + } + } + } + + private final SharedLog mLog = new SharedLog(TAG); + + // used to synchronize public access to members + private final Object mPublicSync; + private final Context mContext; + private final ArrayMap mTetherStates; + private final BroadcastReceiver mStateReceiver; + // Stopship: replace mNMService before production. + private final INetworkManagementService mNMService; + private final INetworkStatsService mStatsService; + private final INetworkPolicyManager mPolicyManager; + private final Looper mLooper; + private final StateMachine mTetherMasterSM; + private final OffloadController mOffloadController; + private final UpstreamNetworkMonitor mUpstreamNetworkMonitor; + // TODO: Figure out how to merge this and other downstream-tracking objects + // into a single coherent structure. + private final HashSet mForwardedDownstreams; + private final VersionedBroadcastListener mCarrierConfigChange; + private final TetheringDependencies mDeps; + private final EntitlementManager mEntitlementMgr; + private final Handler mHandler; + private final PhoneStateListener mPhoneStateListener; + private final INetd mNetd; + private final NetdCallback mNetdCallback; + private int mActiveDataSubId = INVALID_SUBSCRIPTION_ID; + // All the usage of mTetherInternalCallback should run in the same thread. + private ITetherInternalCallback mTetherInternalCallback = null; + + private volatile TetheringConfiguration mConfig; + private InterfaceSet mCurrentUpstreamIfaceSet; + private Notification.Builder mTetheredNotificationBuilder; + private int mLastNotificationId; + + private boolean mRndisEnabled; // track the RNDIS function enabled state + // True iff. WiFi tethering should be started when soft AP is ready. + private boolean mWifiTetherRequested; + private Network mTetherUpstream; + private TetherStatesParcel mTetherStatesParcel; + + public Tethering(TetheringDependencies deps) { + mLog.mark("Tethering.constructed"); + mDeps = deps; + mContext = mDeps.getContext(); + mNMService = mDeps.getINetworkManagementService(); + mStatsService = mDeps.getINetworkStatsService(); + mPolicyManager = mDeps.getINetworkPolicyManager(); + mNetd = mDeps.getINetd(mContext); + mLooper = mDeps.getTetheringLooper(); + + mPublicSync = new Object(); + + mTetherStates = new ArrayMap<>(); + + mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper, deps); + mTetherMasterSM.start(); + + mHandler = mTetherMasterSM.getHandler(); + mOffloadController = new OffloadController(mHandler, + mDeps.getOffloadHardwareInterface(mHandler, mLog), + mContext.getContentResolver(), mNMService, + mLog); + mUpstreamNetworkMonitor = deps.getUpstreamNetworkMonitor(mContext, mTetherMasterSM, mLog, + TetherMasterSM.EVENT_UPSTREAM_CALLBACK); + mForwardedDownstreams = new HashSet<>(); + + IntentFilter filter = new IntentFilter(); + filter.addAction(ACTION_CARRIER_CONFIG_CHANGED); + // EntitlementManager will send EVENT_UPSTREAM_PERMISSION_CHANGED when cellular upstream + // permission is changed according to entitlement check result. + mEntitlementMgr = mDeps.getEntitlementManager(mContext, mTetherMasterSM, mLog, + TetherMasterSM.EVENT_UPSTREAM_PERMISSION_CHANGED); + mEntitlementMgr.setOnUiEntitlementFailedListener((int downstream) -> { + mLog.log("OBSERVED UiEnitlementFailed"); + stopTethering(downstream); + }); + mEntitlementMgr.setTetheringConfigurationFetcher(() -> { + return mConfig; + }); + + mCarrierConfigChange = new VersionedBroadcastListener( + "CarrierConfigChangeListener", mContext, mHandler, filter, + (Intent ignored) -> { + mLog.log("OBSERVED carrier config change"); + updateConfiguration(); + mEntitlementMgr.reevaluateSimCardProvisioning(mConfig); + }); + + mPhoneStateListener = new PhoneStateListener(mLooper) { + @Override + public void onActiveDataSubscriptionIdChanged(int subId) { + mLog.log("OBSERVED active data subscription change, from " + mActiveDataSubId + + " to " + subId); + if (subId == mActiveDataSubId) return; + + mActiveDataSubId = subId; + updateConfiguration(); + // To avoid launching unexpected provisioning checks, ignore re-provisioning when + // no CarrierConfig loaded yet. Assume reevaluateSimCardProvisioning() will be + // triggered again when CarrierConfig is loaded. + if (mEntitlementMgr.getCarrierConfig(mConfig) != null) { + mEntitlementMgr.reevaluateSimCardProvisioning(mConfig); + } else { + mLog.log("IGNORED reevaluate provisioning due to no carrier config loaded"); + } + } + }; + + mStateReceiver = new StateReceiver(); + + mNetdCallback = new NetdCallback(); + try { + mNetd.registerUnsolicitedEventListener(mNetdCallback); + } catch (RemoteException e) { + mLog.e("Unable to register netd UnsolicitedEventListener"); + } + + // Load tethering configuration. + updateConfiguration(); + + startStateMachineUpdaters(mHandler); + startTrackDefaultNetwork(); + } + + private void startStateMachineUpdaters(Handler handler) { + mCarrierConfigChange.startListening(); + mContext.getSystemService(TelephonyManager.class).listen( + mPhoneStateListener, PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE); + + IntentFilter filter = new IntentFilter(); + filter.addAction(UsbManager.ACTION_USB_STATE); + filter.addAction(CONNECTIVITY_ACTION); + filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); + filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); + filter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION); + mContext.registerReceiver(mStateReceiver, filter, null, handler); + + filter = new IntentFilter(); + filter.addAction(Intent.ACTION_MEDIA_SHARED); + filter.addAction(Intent.ACTION_MEDIA_UNSHARED); + filter.addDataScheme("file"); + mContext.registerReceiver(mStateReceiver, filter, null, handler); + + final UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class); + // This check is useful only for some unit tests; example: ConnectivityServiceTest. + if (umi != null) { + umi.addUserRestrictionsListener(new TetheringUserRestrictionListener(this)); + } + } + + private WifiManager getWifiManager() { + return (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); + } + + // NOTE: This is always invoked on the mLooper thread. + private void updateConfiguration() { + mConfig = mDeps.generateTetheringConfiguration(mContext, mLog, mActiveDataSubId); + mUpstreamNetworkMonitor.updateMobileRequiresDun(mConfig.isDunRequired); + reportConfigurationChanged(mConfig.toStableParcelable()); + } + + private void maybeDunSettingChanged() { + final boolean isDunRequired = TetheringConfiguration.checkDunRequired( + mContext, mActiveDataSubId); + if (isDunRequired == mConfig.isDunRequired) return; + updateConfiguration(); + } + + private class NetdCallback extends BaseNetdUnsolicitedEventListener { + @Override + public void onInterfaceChanged(String ifName, boolean up) { + mHandler.post(() -> interfaceStatusChanged(ifName, up)); + } + + @Override + public void onInterfaceLinkStateChanged(String ifName, boolean up) { + mHandler.post(() -> interfaceLinkStateChanged(ifName, up)); + } + + @Override + public void onInterfaceAdded(String ifName) { + mHandler.post(() -> interfaceAdded(ifName)); + } + + @Override + public void onInterfaceRemoved(String ifName) { + mHandler.post(() -> interfaceRemoved(ifName)); + } + } + + void interfaceStatusChanged(String iface, boolean up) { + // Never called directly: only called from interfaceLinkStateChanged. + // See NetlinkHandler.cpp: notifyInterfaceChanged. + if (VDBG) Log.d(TAG, "interfaceStatusChanged " + iface + ", " + up); + synchronized (mPublicSync) { + if (up) { + maybeTrackNewInterfaceLocked(iface); + } else { + if (ifaceNameToType(iface) == TETHERING_BLUETOOTH) { + stopTrackingInterfaceLocked(iface); + } else { + // Ignore usb0 down after enabling RNDIS. + // We will handle disconnect in interfaceRemoved. + // Similarly, ignore interface down for WiFi. We monitor WiFi AP status + // through the WifiManager.WIFI_AP_STATE_CHANGED_ACTION intent. + if (VDBG) Log.d(TAG, "ignore interface down for " + iface); + } + } + } + } + + void interfaceLinkStateChanged(String iface, boolean up) { + interfaceStatusChanged(iface, up); + } + + private int ifaceNameToType(String iface) { + final TetheringConfiguration cfg = mConfig; + + if (cfg.isWifi(iface)) { + return TETHERING_WIFI; + } else if (cfg.isWifiP2p(iface)) { + return TETHERING_WIFI_P2P; + } else if (cfg.isUsb(iface)) { + return TETHERING_USB; + } else if (cfg.isBluetooth(iface)) { + return TETHERING_BLUETOOTH; + } + return TETHERING_INVALID; + } + + void interfaceAdded(String iface) { + if (VDBG) Log.d(TAG, "interfaceAdded " + iface); + synchronized (mPublicSync) { + maybeTrackNewInterfaceLocked(iface); + } + } + + + void interfaceRemoved(String iface) { + if (VDBG) Log.d(TAG, "interfaceRemoved " + iface); + synchronized (mPublicSync) { + stopTrackingInterfaceLocked(iface); + } + } + + void startTethering(int type, ResultReceiver receiver, boolean showProvisioningUi) { + mEntitlementMgr.startProvisioningIfNeeded(type, showProvisioningUi); + enableTetheringInternal(type, true /* enabled */, receiver); + } + + void stopTethering(int type) { + enableTetheringInternal(type, false /* disabled */, null); + mEntitlementMgr.stopProvisioningIfNeeded(type); + } + + /** + * Enables or disables tethering for the given type. If provisioning is required, it will + * schedule provisioning rechecks for the specified interface. + */ + private void enableTetheringInternal(int type, boolean enable, ResultReceiver receiver) { + int result; + switch (type) { + case TETHERING_WIFI: + result = setWifiTethering(enable); + sendTetherResult(receiver, result); + break; + case TETHERING_USB: + result = setUsbTethering(enable); + sendTetherResult(receiver, result); + break; + case TETHERING_BLUETOOTH: + setBluetoothTethering(enable, receiver); + break; + default: + Log.w(TAG, "Invalid tether type."); + sendTetherResult(receiver, TETHER_ERROR_UNKNOWN_IFACE); + } + } + + private void sendTetherResult(ResultReceiver receiver, int result) { + if (receiver != null) { + receiver.send(result, null); + } + } + + private int setWifiTethering(final boolean enable) { + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (mPublicSync) { + final WifiManager mgr = getWifiManager(); + if (mgr == null) { + mLog.e("setWifiTethering: failed to get WifiManager!"); + return TETHER_ERROR_SERVICE_UNAVAIL; + } + if ((enable && mgr.startSoftAp(null /* use existing wifi config */)) + || (!enable && mgr.stopSoftAp())) { + mWifiTetherRequested = enable; + return TETHER_ERROR_NO_ERROR; + } + } + } finally { + Binder.restoreCallingIdentity(ident); + } + + return TETHER_ERROR_MASTER_ERROR; + } + + private void setBluetoothTethering(final boolean enable, final ResultReceiver receiver) { + final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + if (adapter == null || !adapter.isEnabled()) { + Log.w(TAG, "Tried to enable bluetooth tethering with null or disabled adapter. null: " + + (adapter == null)); + sendTetherResult(receiver, TETHER_ERROR_SERVICE_UNAVAIL); + return; + } + + adapter.getProfileProxy(mContext, new ServiceListener() { + @Override + public void onServiceDisconnected(int profile) { } + + @Override + public void onServiceConnected(int profile, BluetoothProfile proxy) { + // Clear identify is fine because caller already pass tethering permission at + // ConnectivityService#startTethering()(or stopTethering) before the control comes + // here. Bluetooth will check tethering permission again that there is + // Context#getOpPackageName() under BluetoothPan#setBluetoothTethering() to get + // caller's package name for permission check. + // Calling BluetoothPan#setBluetoothTethering() here means the package name always + // be system server. If calling identity is not cleared, that package's uid might + // not match calling uid and end up in permission denied. + final long identityToken = Binder.clearCallingIdentity(); + try { + ((BluetoothPan) proxy).setBluetoothTethering(enable); + } finally { + Binder.restoreCallingIdentity(identityToken); + } + // TODO: Enabling bluetooth tethering can fail asynchronously here. + // We should figure out a way to bubble up that failure instead of sending success. + final int result = (((BluetoothPan) proxy).isTetheringOn() == enable) + ? TETHER_ERROR_NO_ERROR + : TETHER_ERROR_MASTER_ERROR; + sendTetherResult(receiver, result); + adapter.closeProfileProxy(BluetoothProfile.PAN, proxy); + } + }, BluetoothProfile.PAN); + } + + int tether(String iface) { + return tether(iface, IpServer.STATE_TETHERED); + } + + private int tether(String iface, int requestedState) { + if (DBG) Log.d(TAG, "Tethering " + iface); + synchronized (mPublicSync) { + TetherState tetherState = mTetherStates.get(iface); + if (tetherState == null) { + Log.e(TAG, "Tried to Tether an unknown iface: " + iface + ", ignoring"); + return TETHER_ERROR_UNKNOWN_IFACE; + } + // Ignore the error status of the interface. If the interface is available, + // the errors are referring to past tethering attempts anyway. + if (tetherState.lastState != IpServer.STATE_AVAILABLE) { + Log.e(TAG, "Tried to Tether an unavailable iface: " + iface + ", ignoring"); + return TETHER_ERROR_UNAVAIL_IFACE; + } + // NOTE: If a CMD_TETHER_REQUESTED message is already in the TISM's + // queue but not yet processed, this will be a no-op and it will not + // return an error. + // + // TODO: reexamine the threading and messaging model. + tetherState.ipServer.sendMessage(IpServer.CMD_TETHER_REQUESTED, requestedState); + return TETHER_ERROR_NO_ERROR; + } + } + + int untether(String iface) { + if (DBG) Log.d(TAG, "Untethering " + iface); + synchronized (mPublicSync) { + TetherState tetherState = mTetherStates.get(iface); + if (tetherState == null) { + Log.e(TAG, "Tried to Untether an unknown iface :" + iface + ", ignoring"); + return TETHER_ERROR_UNKNOWN_IFACE; + } + if (!tetherState.isCurrentlyServing()) { + Log.e(TAG, "Tried to untether an inactive iface :" + iface + ", ignoring"); + return TETHER_ERROR_UNAVAIL_IFACE; + } + tetherState.ipServer.sendMessage(IpServer.CMD_TETHER_UNREQUESTED); + return TETHER_ERROR_NO_ERROR; + } + } + + void untetherAll() { + stopTethering(TETHERING_WIFI); + stopTethering(TETHERING_WIFI_P2P); + stopTethering(TETHERING_USB); + stopTethering(TETHERING_BLUETOOTH); + } + + int getLastTetherError(String iface) { + synchronized (mPublicSync) { + TetherState tetherState = mTetherStates.get(iface); + if (tetherState == null) { + Log.e(TAG, "Tried to getLastTetherError on an unknown iface :" + iface + + ", ignoring"); + return TETHER_ERROR_UNKNOWN_IFACE; + } + return tetherState.lastError; + } + } + + // TODO: Figure out how to update for local hotspot mode interfaces. + private void sendTetherStateChangedBroadcast() { + if (!mDeps.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<>(); + + boolean wifiTethered = false; + boolean usbTethered = false; + boolean bluetoothTethered = false; + + final TetheringConfiguration cfg = mConfig; + final TetherStatesParcel mTetherStatesParcel = new TetherStatesParcel(); + + synchronized (mPublicSync) { + for (int i = 0; i < mTetherStates.size(); i++) { + TetherState tetherState = mTetherStates.valueAt(i); + String iface = mTetherStates.keyAt(i); + if (tetherState.lastError != TETHER_ERROR_NO_ERROR) { + erroredList.add(iface); + lastErrorList.add(tetherState.lastError); + } else if (tetherState.lastState == IpServer.STATE_AVAILABLE) { + availableList.add(iface); + } else if (tetherState.lastState == IpServer.STATE_LOCAL_ONLY) { + localOnlyList.add(iface); + } else if (tetherState.lastState == IpServer.STATE_TETHERED) { + if (cfg.isUsb(iface)) { + usbTethered = true; + } else if (cfg.isWifi(iface)) { + wifiTethered = true; + } else if (cfg.isBluetooth(iface)) { + bluetoothTethered = true; + } + tetherList.add(iface); + } + } + } + + 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(); + } + reportTetherStateChanged(mTetherStatesParcel); + + final Intent bcast = new Intent(ACTION_TETHER_STATE_CHANGED); + bcast.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING + | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + 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); + 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))); + } + + if (usbTethered) { + if (wifiTethered || bluetoothTethered) { + showTetheredNotification(SystemMessage.NOTE_TETHER_GENERAL); + } else { + showTetheredNotification(SystemMessage.NOTE_TETHER_USB); + } + } else if (wifiTethered) { + if (bluetoothTethered) { + showTetheredNotification(SystemMessage.NOTE_TETHER_GENERAL); + } else { + /* We now have a status bar icon for WifiTethering, so drop the notification */ + clearTetheredNotification(); + } + } else if (bluetoothTethered) { + showTetheredNotification(SystemMessage.NOTE_TETHER_BLUETOOTH); + } else { + clearTetheredNotification(); + } + } + + private void showTetheredNotification(int id) { + showTetheredNotification(id, true); + } + + @VisibleForTesting + protected void showTetheredNotification(int id, boolean tetheringOn) { + NotificationManager notificationManager = + (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); + if (notificationManager == null) { + return; + } + int icon = 0; + switch(id) { + case SystemMessage.NOTE_TETHER_USB: + icon = com.android.internal.R.drawable.stat_sys_tether_usb; + break; + case SystemMessage.NOTE_TETHER_BLUETOOTH: + icon = com.android.internal.R.drawable.stat_sys_tether_bluetooth; + break; + case SystemMessage.NOTE_TETHER_GENERAL: + default: + icon = com.android.internal.R.drawable.stat_sys_tether_general; + break; + } + + if (mLastNotificationId != 0) { + if (mLastNotificationId == icon) { + return; + } + notificationManager.cancelAsUser(null, mLastNotificationId, + UserHandle.ALL); + mLastNotificationId = 0; + } + + Intent intent = new Intent(); + intent.setClassName("com.android.settings", "com.android.settings.TetherSettings"); + intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); + + PendingIntent pi = PendingIntent.getActivityAsUser(mContext, 0, intent, 0, + null, UserHandle.CURRENT); + + Resources r = Resources.getSystem(); + final CharSequence title; + final CharSequence message; + + if (tetheringOn) { + title = r.getText(com.android.internal.R.string.tethered_notification_title); + message = r.getText(com.android.internal.R.string.tethered_notification_message); + } else { + title = r.getText(com.android.internal.R.string.disable_tether_notification_title); + message = r.getText(com.android.internal.R.string.disable_tether_notification_message); + } + + if (mTetheredNotificationBuilder == null) { + mTetheredNotificationBuilder = + new Notification.Builder(mContext, SystemNotificationChannels.NETWORK_STATUS); + mTetheredNotificationBuilder.setWhen(0) + .setOngoing(true) + .setColor(mContext.getColor( + com.android.internal.R.color.system_notification_accent_color)) + .setVisibility(Notification.VISIBILITY_PUBLIC) + .setCategory(Notification.CATEGORY_STATUS); + } + mTetheredNotificationBuilder.setSmallIcon(icon) + .setContentTitle(title) + .setContentText(message) + .setContentIntent(pi); + mLastNotificationId = id; + + notificationManager.notifyAsUser(null, mLastNotificationId, + mTetheredNotificationBuilder.buildInto(new Notification()), UserHandle.ALL); + } + + @VisibleForTesting + protected void clearTetheredNotification() { + NotificationManager notificationManager = + (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); + if (notificationManager != null && mLastNotificationId != 0) { + notificationManager.cancelAsUser(null, mLastNotificationId, + UserHandle.ALL); + mLastNotificationId = 0; + } + } + + private class StateReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context content, Intent intent) { + final String action = intent.getAction(); + if (action == null) return; + + if (action.equals(UsbManager.ACTION_USB_STATE)) { + handleUsbAction(intent); + } else if (action.equals(CONNECTIVITY_ACTION)) { + handleConnectivityAction(intent); + } else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) { + handleWifiApAction(intent); + } else if (action.equals(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)) { + handleWifiP2pAction(intent); + } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) { + mLog.log("OBSERVED configuration changed"); + updateConfiguration(); + } + } + + private void handleConnectivityAction(Intent intent) { + final NetworkInfo networkInfo = + (NetworkInfo) intent.getParcelableExtra(EXTRA_NETWORK_INFO); + if (networkInfo == null + || networkInfo.getDetailedState() == NetworkInfo.DetailedState.FAILED) { + return; + } + + if (VDBG) Log.d(TAG, "Tethering got CONNECTIVITY_ACTION: " + networkInfo.toString()); + mTetherMasterSM.sendMessage(TetherMasterSM.CMD_UPSTREAM_CHANGED); + } + + private void handleUsbAction(Intent intent) { + final boolean usbConnected = intent.getBooleanExtra(USB_CONNECTED, false); + final boolean usbConfigured = intent.getBooleanExtra(USB_CONFIGURED, false); + final boolean rndisEnabled = intent.getBooleanExtra(USB_FUNCTION_RNDIS, false); + + mLog.log(String.format("USB bcast connected:%s configured:%s rndis:%s", + usbConnected, usbConfigured, rndisEnabled)); + + // There are three types of ACTION_USB_STATE: + // + // - DISCONNECTED (USB_CONNECTED and USB_CONFIGURED are 0) + // Meaning: USB connection has ended either because of + // software reset or hard unplug. + // + // - CONNECTED (USB_CONNECTED is 1, USB_CONFIGURED is 0) + // Meaning: the first stage of USB protocol handshake has + // occurred but it is not complete. + // + // - CONFIGURED (USB_CONNECTED and USB_CONFIGURED are 1) + // Meaning: the USB handshake is completely done and all the + // functions are ready to use. + // + // For more explanation, see b/62552150 . + synchronized (Tethering.this.mPublicSync) { + if (!usbConnected && mRndisEnabled) { + // Turn off tethering if it was enabled and there is a disconnect. + tetherMatchingInterfaces(IpServer.STATE_AVAILABLE, TETHERING_USB); + mEntitlementMgr.stopProvisioningIfNeeded(TETHERING_USB); + } else if (usbConfigured && rndisEnabled) { + // Tether if rndis is enabled and usb is configured. + tetherMatchingInterfaces(IpServer.STATE_TETHERED, TETHERING_USB); + } + mRndisEnabled = usbConfigured && rndisEnabled; + } + } + + private void handleWifiApAction(Intent intent) { + final int curState = intent.getIntExtra(EXTRA_WIFI_AP_STATE, WIFI_AP_STATE_DISABLED); + final String ifname = intent.getStringExtra(EXTRA_WIFI_AP_INTERFACE_NAME); + final int ipmode = intent.getIntExtra(EXTRA_WIFI_AP_MODE, IFACE_IP_MODE_UNSPECIFIED); + + synchronized (Tethering.this.mPublicSync) { + switch (curState) { + case WifiManager.WIFI_AP_STATE_ENABLING: + // We can see this state on the way to both enabled and failure states. + break; + case WifiManager.WIFI_AP_STATE_ENABLED: + enableWifiIpServingLocked(ifname, ipmode); + break; + case WifiManager.WIFI_AP_STATE_DISABLED: + case WifiManager.WIFI_AP_STATE_DISABLING: + case WifiManager.WIFI_AP_STATE_FAILED: + default: + disableWifiIpServingLocked(ifname, curState); + break; + } + } + } + + private void handleWifiP2pAction(Intent intent) { + if (mConfig.isWifiP2pLegacyTetheringMode()) return; + + final WifiP2pInfo p2pInfo = + (WifiP2pInfo) intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO); + final WifiP2pGroup group = + (WifiP2pGroup) intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP); + + if (VDBG) { + Log.d(TAG, "WifiP2pAction: P2pInfo: " + p2pInfo + " Group: " + group); + } + + if (p2pInfo == null) return; + // When a p2p group is disconnected, p2pInfo would be cleared. + // group is still valid for detecting whether this device is group owner. + if (group == null || !group.isGroupOwner() + || TextUtils.isEmpty(group.getInterface())) return; + + synchronized (Tethering.this.mPublicSync) { + // Enter below only if this device is Group Owner with a valid interface. + if (p2pInfo.groupFormed) { + TetherState tetherState = mTetherStates.get(group.getInterface()); + if (tetherState == null + || (tetherState.lastState != IpServer.STATE_TETHERED + && tetherState.lastState != IpServer.STATE_LOCAL_ONLY)) { + enableWifiIpServingLocked(group.getInterface(), IFACE_IP_MODE_LOCAL_ONLY); + } + } else { + disableWifiP2pIpServingLocked(group.getInterface()); + } + } + } + } + + @VisibleForTesting + protected static class TetheringUserRestrictionListener implements UserRestrictionsListener { + private final Tethering mWrapper; + + public TetheringUserRestrictionListener(Tethering wrapper) { + mWrapper = wrapper; + } + + public void onUserRestrictionsChanged(int userId, + Bundle newRestrictions, + Bundle prevRestrictions) { + final boolean newlyDisallowed = + newRestrictions.getBoolean(UserManager.DISALLOW_CONFIG_TETHERING); + final boolean previouslyDisallowed = + prevRestrictions.getBoolean(UserManager.DISALLOW_CONFIG_TETHERING); + final boolean tetheringDisallowedChanged = (newlyDisallowed != previouslyDisallowed); + + if (!tetheringDisallowedChanged) { + return; + } + + mWrapper.clearTetheredNotification(); + final boolean isTetheringActiveOnDevice = (mWrapper.getTetheredIfaces().length != 0); + + if (newlyDisallowed && isTetheringActiveOnDevice) { + mWrapper.showTetheredNotification( + com.android.internal.R.drawable.stat_sys_tether_general, false); + mWrapper.untetherAll(); + } + } + } + + private void disableWifiIpServingLockedCommon(int tetheringType, String ifname, int apState) { + mLog.log("Canceling WiFi tethering request -" + + " type=" + tetheringType + + " interface=" + ifname + + " state=" + apState); + + if (!TextUtils.isEmpty(ifname)) { + final TetherState ts = mTetherStates.get(ifname); + if (ts != null) { + ts.ipServer.unwanted(); + return; + } + } + + for (int i = 0; i < mTetherStates.size(); i++) { + final IpServer ipServer = mTetherStates.valueAt(i).ipServer; + if (ipServer.interfaceType() == tetheringType) { + ipServer.unwanted(); + return; + } + } + + mLog.log("Error disabling Wi-Fi IP serving; " + + (TextUtils.isEmpty(ifname) ? "no interface name specified" + : "specified interface: " + ifname)); + } + + private void disableWifiIpServingLocked(String ifname, int apState) { + // Regardless of whether we requested this transition, the AP has gone + // down. Don't try to tether again unless we're requested to do so. + // TODO: Remove this altogether, once Wi-Fi reliably gives us an + // interface name with every broadcast. + mWifiTetherRequested = false; + + disableWifiIpServingLockedCommon(TETHERING_WIFI, ifname, apState); + } + + private void disableWifiP2pIpServingLocked(String ifname) { + disableWifiIpServingLockedCommon(TETHERING_WIFI_P2P, ifname, /* dummy */ 0); + } + + private void enableWifiIpServingLocked(String ifname, int wifiIpMode) { + // Map wifiIpMode values to IpServer.Callback serving states, inferring + // from mWifiTetherRequested as a final "best guess". + final int ipServingMode; + switch (wifiIpMode) { + case IFACE_IP_MODE_TETHERED: + ipServingMode = IpServer.STATE_TETHERED; + break; + case IFACE_IP_MODE_LOCAL_ONLY: + ipServingMode = IpServer.STATE_LOCAL_ONLY; + break; + default: + mLog.e("Cannot enable IP serving in unknown WiFi mode: " + wifiIpMode); + return; + } + + if (!TextUtils.isEmpty(ifname)) { + maybeTrackNewInterfaceLocked(ifname); + changeInterfaceState(ifname, ipServingMode); + } else { + mLog.e(String.format( + "Cannot enable IP serving in mode %s on missing interface name", + ipServingMode)); + } + } + + // TODO: Consider renaming to something more accurate in its description. + // This method: + // - allows requesting either tethering or local hotspot serving states + // - handles both enabling and disabling serving states + // - only tethers the first matching interface in listInterfaces() + // order of a given type + private void tetherMatchingInterfaces(int requestedState, int interfaceType) { + if (VDBG) { + Log.d(TAG, "tetherMatchingInterfaces(" + requestedState + ", " + interfaceType + ")"); + } + + String[] ifaces = null; + try { + ifaces = mNMService.listInterfaces(); + } catch (Exception e) { + Log.e(TAG, "Error listing Interfaces", e); + return; + } + String chosenIface = null; + if (ifaces != null) { + for (String iface : ifaces) { + if (ifaceNameToType(iface) == interfaceType) { + chosenIface = iface; + break; + } + } + } + if (chosenIface == null) { + Log.e(TAG, "could not find iface of type " + interfaceType); + return; + } + + changeInterfaceState(chosenIface, requestedState); + } + + private void changeInterfaceState(String ifname, int requestedState) { + final int result; + switch (requestedState) { + case IpServer.STATE_UNAVAILABLE: + case IpServer.STATE_AVAILABLE: + result = untether(ifname); + break; + case IpServer.STATE_TETHERED: + case IpServer.STATE_LOCAL_ONLY: + result = tether(ifname, requestedState); + break; + default: + Log.wtf(TAG, "Unknown interface state: " + requestedState); + return; + } + if (result != TETHER_ERROR_NO_ERROR) { + Log.e(TAG, "unable start or stop tethering on iface " + ifname); + return; + } + } + + TetheringConfiguration getTetheringConfiguration() { + return mConfig; + } + + boolean hasTetherableConfiguration() { + final TetheringConfiguration cfg = mConfig; + final boolean hasDownstreamConfiguration = + (cfg.tetherableUsbRegexs.length != 0) + || (cfg.tetherableWifiRegexs.length != 0) + || (cfg.tetherableBluetoothRegexs.length != 0); + final boolean hasUpstreamConfiguration = !cfg.preferredUpstreamIfaceTypes.isEmpty() + || cfg.chooseUpstreamAutomatically; + + return hasDownstreamConfiguration && hasUpstreamConfiguration; + } + + // TODO - update callers to use getTetheringConfiguration(), + // which has only final members. + String[] getTetherableUsbRegexs() { + return copy(mConfig.tetherableUsbRegexs); + } + + String[] getTetherableWifiRegexs() { + return copy(mConfig.tetherableWifiRegexs); + } + + String[] getTetherableBluetoothRegexs() { + return copy(mConfig.tetherableBluetoothRegexs); + } + + int setUsbTethering(boolean enable) { + if (VDBG) Log.d(TAG, "setUsbTethering(" + enable + ")"); + UsbManager usbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE); + if (usbManager == null) { + mLog.e("setUsbTethering: failed to get UsbManager!"); + return TETHER_ERROR_SERVICE_UNAVAIL; + } + + synchronized (mPublicSync) { + usbManager.setCurrentFunctions(enable ? UsbManager.FUNCTION_RNDIS + : UsbManager.FUNCTION_NONE); + } + return TETHER_ERROR_NO_ERROR; + } + + // TODO review API - figure out how to delete these entirely. + String[] getTetheredIfaces() { + ArrayList list = new ArrayList(); + synchronized (mPublicSync) { + for (int i = 0; i < mTetherStates.size(); i++) { + TetherState tetherState = mTetherStates.valueAt(i); + if (tetherState.lastState == IpServer.STATE_TETHERED) { + list.add(mTetherStates.keyAt(i)); + } + } + } + return list.toArray(new String[list.size()]); + } + + String[] getTetherableIfaces() { + ArrayList list = new ArrayList(); + synchronized (mPublicSync) { + for (int i = 0; i < mTetherStates.size(); i++) { + TetherState tetherState = mTetherStates.valueAt(i); + if (tetherState.lastState == IpServer.STATE_AVAILABLE) { + list.add(mTetherStates.keyAt(i)); + } + } + } + return list.toArray(new String[list.size()]); + } + + String[] getTetheredDhcpRanges() { + // TODO: this is only valid for the old DHCP server. Latest search suggests it is only used + // by WifiP2pServiceImpl to start dnsmasq: remove/deprecate after migrating callers. + return mConfig.legacyDhcpRanges; + } + + String[] getErroredIfaces() { + ArrayList list = new ArrayList(); + synchronized (mPublicSync) { + for (int i = 0; i < mTetherStates.size(); i++) { + TetherState tetherState = mTetherStates.valueAt(i); + if (tetherState.lastError != TETHER_ERROR_NO_ERROR) { + list.add(mTetherStates.keyAt(i)); + } + } + } + return list.toArray(new String[list.size()]); + } + + private void logMessage(State state, int what) { + mLog.log(state.getName() + " got " + sMagicDecoderRing.get(what, Integer.toString(what))); + } + + private boolean upstreamWanted() { + if (!mForwardedDownstreams.isEmpty()) return true; + + synchronized (mPublicSync) { + return mWifiTetherRequested; + } + } + + // Needed because the canonical source of upstream truth is just the + // upstream interface set, |mCurrentUpstreamIfaceSet|. + private boolean pertainsToCurrentUpstream(NetworkState ns) { + if (ns != null && ns.linkProperties != null && mCurrentUpstreamIfaceSet != null) { + for (String ifname : ns.linkProperties.getAllInterfaceNames()) { + if (mCurrentUpstreamIfaceSet.ifnames.contains(ifname)) { + return true; + } + } + } + return false; + } + + class TetherMasterSM extends StateMachine { + private static final int BASE_MASTER = Protocol.BASE_TETHERING; + // an interface SM has requested Tethering/Local Hotspot + static final int EVENT_IFACE_SERVING_STATE_ACTIVE = BASE_MASTER + 1; + // an interface SM has unrequested Tethering/Local Hotspot + static final int EVENT_IFACE_SERVING_STATE_INACTIVE = BASE_MASTER + 2; + // upstream connection change - do the right thing + static final int CMD_UPSTREAM_CHANGED = BASE_MASTER + 3; + // we don't have a valid upstream conn, check again after a delay + static final int CMD_RETRY_UPSTREAM = BASE_MASTER + 4; + // Events from NetworkCallbacks that we process on the master state + // machine thread on behalf of the UpstreamNetworkMonitor. + static final int EVENT_UPSTREAM_CALLBACK = BASE_MASTER + 5; + // we treated the error and want now to clear it + static final int CMD_CLEAR_ERROR = BASE_MASTER + 6; + static final int EVENT_IFACE_UPDATE_LINKPROPERTIES = BASE_MASTER + 7; + // Events from EntitlementManager to choose upstream again. + static final int EVENT_UPSTREAM_PERMISSION_CHANGED = BASE_MASTER + 8; + + private final State mInitialState; + private final State mTetherModeAliveState; + + private final State mSetIpForwardingEnabledErrorState; + private final State mSetIpForwardingDisabledErrorState; + private final State mStartTetheringErrorState; + private final State mStopTetheringErrorState; + private final State mSetDnsForwardersErrorState; + + // This list is a little subtle. It contains all the interfaces that currently are + // requesting tethering, regardless of whether these interfaces are still members of + // mTetherStates. This allows us to maintain the following predicates: + // + // 1) mTetherStates contains the set of all currently existing, tetherable, link state up + // interfaces. + // 2) mNotifyList contains all state machines that may have outstanding tethering state + // that needs to be torn down. + // + // Because we excise interfaces immediately from mTetherStates, we must maintain mNotifyList + // so that the garbage collector does not clean up the state machine before it has a chance + // to tear itself down. + private final ArrayList mNotifyList; + private final IPv6TetheringCoordinator mIPv6TetheringCoordinator; + private final OffloadWrapper mOffload; + + private static final int UPSTREAM_SETTLE_TIME_MS = 10000; + + TetherMasterSM(String name, Looper looper, TetheringDependencies deps) { + super(name, looper); + + mInitialState = new InitialState(); + mTetherModeAliveState = new TetherModeAliveState(); + mSetIpForwardingEnabledErrorState = new SetIpForwardingEnabledErrorState(); + mSetIpForwardingDisabledErrorState = new SetIpForwardingDisabledErrorState(); + mStartTetheringErrorState = new StartTetheringErrorState(); + mStopTetheringErrorState = new StopTetheringErrorState(); + mSetDnsForwardersErrorState = new SetDnsForwardersErrorState(); + + addState(mInitialState); + addState(mTetherModeAliveState); + addState(mSetIpForwardingEnabledErrorState); + addState(mSetIpForwardingDisabledErrorState); + addState(mStartTetheringErrorState); + addState(mStopTetheringErrorState); + addState(mSetDnsForwardersErrorState); + + mNotifyList = new ArrayList<>(); + mIPv6TetheringCoordinator = deps.getIPv6TetheringCoordinator(mNotifyList, mLog); + mOffload = new OffloadWrapper(); + + setInitialState(mInitialState); + } + + class InitialState extends State { + @Override + public boolean processMessage(Message message) { + logMessage(this, message.what); + switch (message.what) { + case EVENT_IFACE_SERVING_STATE_ACTIVE: { + final IpServer who = (IpServer) message.obj; + if (VDBG) Log.d(TAG, "Tether Mode requested by " + who); + handleInterfaceServingStateActive(message.arg1, who); + transitionTo(mTetherModeAliveState); + break; + } + case EVENT_IFACE_SERVING_STATE_INACTIVE: { + final IpServer who = (IpServer) message.obj; + if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who); + handleInterfaceServingStateInactive(who); + break; + } + case EVENT_IFACE_UPDATE_LINKPROPERTIES: + // Silently ignore these for now. + break; + default: + return NOT_HANDLED; + } + return HANDLED; + } + } + + protected boolean turnOnMasterTetherSettings() { + final TetheringConfiguration cfg = mConfig; + try { + mNMService.setIpForwardingEnabled(true); + } catch (Exception e) { + mLog.e(e); + transitionTo(mSetIpForwardingEnabledErrorState); + return false; + } + // TODO: Randomize DHCPv4 ranges, especially in hotspot mode. + // Legacy DHCP server is disabled if passed an empty ranges array + final String[] dhcpRanges = cfg.enableLegacyDhcpServer + ? cfg.legacyDhcpRanges + : new String[0]; + try { + // TODO: Find a more accurate method name (startDHCPv4()?). + mNMService.startTethering(dhcpRanges); + } catch (Exception e) { + try { + mNMService.stopTethering(); + mNMService.startTethering(dhcpRanges); + } catch (Exception ee) { + mLog.e(ee); + transitionTo(mStartTetheringErrorState); + return false; + } + } + mLog.log("SET master tether settings: ON"); + return true; + } + + protected boolean turnOffMasterTetherSettings() { + try { + mNMService.stopTethering(); + } catch (Exception e) { + mLog.e(e); + transitionTo(mStopTetheringErrorState); + return false; + } + try { + mNMService.setIpForwardingEnabled(false); + } catch (Exception e) { + mLog.e(e); + transitionTo(mSetIpForwardingDisabledErrorState); + return false; + } + transitionTo(mInitialState); + mLog.log("SET master tether settings: OFF"); + return true; + } + + protected void chooseUpstreamType(boolean tryCell) { + // We rebuild configuration on ACTION_CONFIGURATION_CHANGED, but we + // do not currently know how to watch for changes in DUN settings. + maybeDunSettingChanged(); + + final TetheringConfiguration config = mConfig; + final NetworkState ns = (config.chooseUpstreamAutomatically) + ? mUpstreamNetworkMonitor.getCurrentPreferredUpstream() + : mUpstreamNetworkMonitor.selectPreferredUpstreamType( + config.preferredUpstreamIfaceTypes); + if (ns == null) { + if (tryCell) { + mUpstreamNetworkMonitor.registerMobileNetworkRequest(); + // We think mobile should be coming up; don't set a retry. + } else { + sendMessageDelayed(CMD_RETRY_UPSTREAM, UPSTREAM_SETTLE_TIME_MS); + } + } + setUpstreamNetwork(ns); + final Network newUpstream = (ns != null) ? ns.network : null; + if (mTetherUpstream != newUpstream) { + mTetherUpstream = newUpstream; + mUpstreamNetworkMonitor.setCurrentUpstream(mTetherUpstream); + reportUpstreamChanged(mTetherUpstream); + } + } + + protected void setUpstreamNetwork(NetworkState ns) { + InterfaceSet ifaces = null; + if (ns != null) { + // Find the interface with the default IPv4 route. It may be the + // interface described by linkProperties, or one of the interfaces + // stacked on top of it. + mLog.i("Looking for default routes on: " + ns.linkProperties); + ifaces = TetheringInterfaceUtils.getTetheringInterfaces(ns); + mLog.i("Found upstream interface(s): " + ifaces); + } + + if (ifaces != null) { + setDnsForwarders(ns.network, ns.linkProperties); + } + notifyDownstreamsOfNewUpstreamIface(ifaces); + if (ns != null && pertainsToCurrentUpstream(ns)) { + // If we already have NetworkState for this network update it immediately. + handleNewUpstreamNetworkState(ns); + } else if (mCurrentUpstreamIfaceSet == null) { + // There are no available upstream networks. + handleNewUpstreamNetworkState(null); + } + } + + protected void setDnsForwarders(final Network network, final LinkProperties lp) { + // TODO: Set v4 and/or v6 DNS per available connectivity. + String[] dnsServers = mConfig.defaultIPv4DNS; + final Collection dnses = lp.getDnsServers(); + // TODO: Properly support the absence of DNS servers. + if (dnses != null && !dnses.isEmpty()) { + // TODO: remove this invocation of NetworkUtils.makeStrings(). + dnsServers = NetworkUtils.makeStrings(dnses); + } + try { + mNMService.setDnsForwarders(network, dnsServers); + mLog.log(String.format( + "SET DNS forwarders: network=%s dnsServers=%s", + network, Arrays.toString(dnsServers))); + } catch (Exception e) { + // TODO: Investigate how this can fail and what exactly + // happens if/when such failures occur. + mLog.e("setting DNS forwarders failed, " + e); + transitionTo(mSetDnsForwardersErrorState); + } + } + + protected void notifyDownstreamsOfNewUpstreamIface(InterfaceSet ifaces) { + mCurrentUpstreamIfaceSet = ifaces; + for (IpServer ipServer : mNotifyList) { + ipServer.sendMessage(IpServer.CMD_TETHER_CONNECTION_CHANGED, ifaces); + } + } + + protected void handleNewUpstreamNetworkState(NetworkState ns) { + mIPv6TetheringCoordinator.updateUpstreamNetworkState(ns); + mOffload.updateUpstreamNetworkState(ns); + } + + private void handleInterfaceServingStateActive(int mode, IpServer who) { + if (mNotifyList.indexOf(who) < 0) { + mNotifyList.add(who); + mIPv6TetheringCoordinator.addActiveDownstream(who, mode); + } + + if (mode == IpServer.STATE_TETHERED) { + // No need to notify OffloadController just yet as there are no + // "offload-able" prefixes to pass along. This will handled + // when the TISM informs Tethering of its LinkProperties. + mForwardedDownstreams.add(who); + } else { + mOffload.excludeDownstreamInterface(who.interfaceName()); + mForwardedDownstreams.remove(who); + } + + // If this is a Wi-Fi interface, notify WifiManager of the active serving state. + if (who.interfaceType() == TETHERING_WIFI) { + final WifiManager mgr = getWifiManager(); + final String iface = who.interfaceName(); + switch (mode) { + case IpServer.STATE_TETHERED: + mgr.updateInterfaceIpState(iface, IFACE_IP_MODE_TETHERED); + break; + case IpServer.STATE_LOCAL_ONLY: + mgr.updateInterfaceIpState(iface, IFACE_IP_MODE_LOCAL_ONLY); + break; + default: + Log.wtf(TAG, "Unknown active serving mode: " + mode); + break; + } + } + } + + private void handleInterfaceServingStateInactive(IpServer who) { + mNotifyList.remove(who); + mIPv6TetheringCoordinator.removeActiveDownstream(who); + mOffload.excludeDownstreamInterface(who.interfaceName()); + mForwardedDownstreams.remove(who); + + // If this is a Wi-Fi interface, tell WifiManager of any errors + // or the inactive serving state. + if (who.interfaceType() == TETHERING_WIFI) { + if (who.lastError() != TETHER_ERROR_NO_ERROR) { + getWifiManager().updateInterfaceIpState( + who.interfaceName(), IFACE_IP_MODE_CONFIGURATION_ERROR); + } else { + getWifiManager().updateInterfaceIpState( + who.interfaceName(), IFACE_IP_MODE_UNSPECIFIED); + } + } + } + + private void handleUpstreamNetworkMonitorCallback(int arg1, Object o) { + if (arg1 == UpstreamNetworkMonitor.NOTIFY_LOCAL_PREFIXES) { + mOffload.sendOffloadExemptPrefixes((Set) o); + return; + } + + final NetworkState ns = (NetworkState) o; + + if (ns == null || !pertainsToCurrentUpstream(ns)) { + // TODO: In future, this is where upstream evaluation and selection + // could be handled for notifications which include sufficient data. + // For example, after CONNECTIVITY_ACTION listening is removed, here + // is where we could observe a Wi-Fi network becoming available and + // passing validation. + if (mCurrentUpstreamIfaceSet == null) { + // If we have no upstream interface, try to run through upstream + // selection again. If, for example, IPv4 connectivity has shown up + // after IPv6 (e.g., 464xlat became available) we want the chance to + // notice and act accordingly. + chooseUpstreamType(false); + } + return; + } + + switch (arg1) { + case UpstreamNetworkMonitor.EVENT_ON_CAPABILITIES: + handleNewUpstreamNetworkState(ns); + break; + case UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES: + chooseUpstreamType(false); + break; + case UpstreamNetworkMonitor.EVENT_ON_LOST: + // TODO: Re-evaluate possible upstreams. Currently upstream + // reevaluation is triggered via received CONNECTIVITY_ACTION + // broadcasts that result in being passed a + // TetherMasterSM.CMD_UPSTREAM_CHANGED. + handleNewUpstreamNetworkState(null); + break; + default: + mLog.e("Unknown arg1 value: " + arg1); + break; + } + } + + class TetherModeAliveState extends State { + boolean mUpstreamWanted = false; + boolean mTryCell = true; + + @Override + public void enter() { + // If turning on master tether settings fails, we have already + // transitioned to an error state; exit early. + if (!turnOnMasterTetherSettings()) { + return; + } + + mUpstreamNetworkMonitor.startObserveAllNetworks(); + + // TODO: De-duplicate with updateUpstreamWanted() below. + if (upstreamWanted()) { + mUpstreamWanted = true; + mOffload.start(); + chooseUpstreamType(true); + mTryCell = false; + } + } + + @Override + public void exit() { + mOffload.stop(); + mUpstreamNetworkMonitor.stop(); + notifyDownstreamsOfNewUpstreamIface(null); + handleNewUpstreamNetworkState(null); + if (mTetherUpstream != null) { + mTetherUpstream = null; + reportUpstreamChanged(null); + } + } + + private boolean updateUpstreamWanted() { + final boolean previousUpstreamWanted = mUpstreamWanted; + mUpstreamWanted = upstreamWanted(); + if (mUpstreamWanted != previousUpstreamWanted) { + if (mUpstreamWanted) { + mOffload.start(); + } else { + mOffload.stop(); + } + } + return previousUpstreamWanted; + } + + @Override + public boolean processMessage(Message message) { + logMessage(this, message.what); + boolean retValue = true; + switch (message.what) { + case EVENT_IFACE_SERVING_STATE_ACTIVE: { + IpServer who = (IpServer) message.obj; + if (VDBG) Log.d(TAG, "Tether Mode requested by " + who); + handleInterfaceServingStateActive(message.arg1, who); + who.sendMessage(IpServer.CMD_TETHER_CONNECTION_CHANGED, + mCurrentUpstreamIfaceSet); + // If there has been a change and an upstream is now + // desired, kick off the selection process. + final boolean previousUpstreamWanted = updateUpstreamWanted(); + if (!previousUpstreamWanted && mUpstreamWanted) { + chooseUpstreamType(true); + } + break; + } + case EVENT_IFACE_SERVING_STATE_INACTIVE: { + IpServer who = (IpServer) message.obj; + if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who); + handleInterfaceServingStateInactive(who); + + if (mNotifyList.isEmpty()) { + // This transitions us out of TetherModeAliveState, + // either to InitialState or an error state. + turnOffMasterTetherSettings(); + break; + } + + if (DBG) { + Log.d(TAG, "TetherModeAlive still has " + mNotifyList.size() + + " live requests:"); + for (IpServer o : mNotifyList) { + Log.d(TAG, " " + o); + } + } + // If there has been a change and an upstream is no + // longer desired, release any mobile requests. + final boolean previousUpstreamWanted = updateUpstreamWanted(); + if (previousUpstreamWanted && !mUpstreamWanted) { + mUpstreamNetworkMonitor.releaseMobileNetworkRequest(); + } + break; + } + case EVENT_IFACE_UPDATE_LINKPROPERTIES: { + final LinkProperties newLp = (LinkProperties) message.obj; + if (message.arg1 == IpServer.STATE_TETHERED) { + mOffload.updateDownstreamLinkProperties(newLp); + } else { + mOffload.excludeDownstreamInterface(newLp.getInterfaceName()); + } + break; + } + case EVENT_UPSTREAM_PERMISSION_CHANGED: + case CMD_UPSTREAM_CHANGED: + updateUpstreamWanted(); + if (!mUpstreamWanted) break; + + // Need to try DUN immediately if Wi-Fi goes down. + chooseUpstreamType(true); + mTryCell = false; + break; + case CMD_RETRY_UPSTREAM: + updateUpstreamWanted(); + if (!mUpstreamWanted) break; + + chooseUpstreamType(mTryCell); + mTryCell = !mTryCell; + break; + case EVENT_UPSTREAM_CALLBACK: { + updateUpstreamWanted(); + if (mUpstreamWanted) { + handleUpstreamNetworkMonitorCallback(message.arg1, message.obj); + } + break; + } + default: + retValue = false; + break; + } + return retValue; + } + } + + class ErrorState extends State { + private int mErrorNotification; + + @Override + public boolean processMessage(Message message) { + boolean retValue = true; + switch (message.what) { + case EVENT_IFACE_SERVING_STATE_ACTIVE: + IpServer who = (IpServer) message.obj; + who.sendMessage(mErrorNotification); + break; + case CMD_CLEAR_ERROR: + mErrorNotification = TETHER_ERROR_NO_ERROR; + transitionTo(mInitialState); + break; + default: + retValue = false; + } + return retValue; + } + + void notify(int msgType) { + mErrorNotification = msgType; + for (IpServer ipServer : mNotifyList) { + ipServer.sendMessage(msgType); + } + } + + } + + class SetIpForwardingEnabledErrorState extends ErrorState { + @Override + public void enter() { + Log.e(TAG, "Error in setIpForwardingEnabled"); + notify(IpServer.CMD_IP_FORWARDING_ENABLE_ERROR); + } + } + + class SetIpForwardingDisabledErrorState extends ErrorState { + @Override + public void enter() { + Log.e(TAG, "Error in setIpForwardingDisabled"); + notify(IpServer.CMD_IP_FORWARDING_DISABLE_ERROR); + } + } + + class StartTetheringErrorState extends ErrorState { + @Override + public void enter() { + Log.e(TAG, "Error in startTethering"); + notify(IpServer.CMD_START_TETHERING_ERROR); + try { + mNMService.setIpForwardingEnabled(false); + } catch (Exception e) { } + } + } + + class StopTetheringErrorState extends ErrorState { + @Override + public void enter() { + Log.e(TAG, "Error in stopTethering"); + notify(IpServer.CMD_STOP_TETHERING_ERROR); + try { + mNMService.setIpForwardingEnabled(false); + } catch (Exception e) { } + } + } + + class SetDnsForwardersErrorState extends ErrorState { + @Override + public void enter() { + Log.e(TAG, "Error in setDnsForwarders"); + notify(IpServer.CMD_SET_DNS_FORWARDERS_ERROR); + try { + mNMService.stopTethering(); + } catch (Exception e) { } + try { + mNMService.setIpForwardingEnabled(false); + } catch (Exception e) { } + } + } + + // A wrapper class to handle multiple situations where several calls to + // the OffloadController need to happen together. + // + // TODO: This suggests that the interface between OffloadController and + // Tethering is in need of improvement. Refactor these calls into the + // OffloadController implementation. + class OffloadWrapper { + public void start() { + mOffloadController.start(); + sendOffloadExemptPrefixes(); + } + + public void stop() { + mOffloadController.stop(); + } + + public void updateUpstreamNetworkState(NetworkState ns) { + mOffloadController.setUpstreamLinkProperties( + (ns != null) ? ns.linkProperties : null); + } + + public void updateDownstreamLinkProperties(LinkProperties newLp) { + // Update the list of offload-exempt prefixes before adding + // new prefixes on downstream interfaces to the offload HAL. + sendOffloadExemptPrefixes(); + mOffloadController.notifyDownstreamLinkProperties(newLp); + } + + public void excludeDownstreamInterface(String ifname) { + // This and other interfaces may be in local-only hotspot mode; + // resend all local prefixes to the OffloadController. + sendOffloadExemptPrefixes(); + mOffloadController.removeDownstreamInterface(ifname); + } + + public void sendOffloadExemptPrefixes() { + sendOffloadExemptPrefixes(mUpstreamNetworkMonitor.getLocalPrefixes()); + } + + public void sendOffloadExemptPrefixes(final Set localPrefixes) { + // Add in well-known minimum set. + PrefixUtils.addNonForwardablePrefixes(localPrefixes); + // Add tragically hardcoded prefixes. + localPrefixes.add(PrefixUtils.DEFAULT_WIFI_P2P_PREFIX); + + // Maybe add prefixes or addresses for downstreams, depending on + // the IP serving mode of each. + for (IpServer ipServer : mNotifyList) { + final LinkProperties lp = ipServer.linkProperties(); + + switch (ipServer.servingMode()) { + case IpServer.STATE_UNAVAILABLE: + case IpServer.STATE_AVAILABLE: + // No usable LinkProperties in these states. + continue; + case IpServer.STATE_TETHERED: + // Only add IPv4 /32 and IPv6 /128 prefixes. The + // directly-connected prefixes will be sent as + // downstream "offload-able" prefixes. + for (LinkAddress addr : lp.getAllLinkAddresses()) { + final InetAddress ip = addr.getAddress(); + if (ip.isLinkLocalAddress()) continue; + localPrefixes.add(PrefixUtils.ipAddressAsPrefix(ip)); + } + break; + case IpServer.STATE_LOCAL_ONLY: + // Add prefixes covering all local IPs. + localPrefixes.addAll(PrefixUtils.localPrefixesFrom(lp)); + break; + } + } + + mOffloadController.setLocalPrefixes(localPrefixes); + } + } + } + + private void startTrackDefaultNetwork() { + mUpstreamNetworkMonitor.startTrackDefaultNetwork(mDeps.getDefaultNetworkRequest(), + mEntitlementMgr); + } + + /** Get the latest value of the tethering entitlement check. */ + void requestLatestTetheringEntitlementResult(int type, ResultReceiver receiver, + boolean showEntitlementUi) { + if (receiver != null) { + mEntitlementMgr.requestLatestTetheringEntitlementResult(type, receiver, + showEntitlementUi); + } + } + + /** Register tethering event callback */ + void registerTetherInternalCallback(ITetherInternalCallback callback) { + mHandler.post(() -> { + mTetherInternalCallback = callback; + try { + mTetherInternalCallback.onCallbackCreated(mTetherUpstream, + mConfig.toStableParcelable(), mTetherStatesParcel); + } catch (RemoteException e) { + // Not really very much to do here. + } + }); + } + + private void reportUpstreamChanged(Network network) { + // Don't need to synchronized mTetherInternalCallback because all the usage of this variable + // should run at the same thread. + if (mTetherInternalCallback == null) return; + + try { + mTetherInternalCallback.onUpstreamChanged(network); + } catch (RemoteException e) { + // Not really very much to do here. + } + } + + private void reportConfigurationChanged(TetheringConfigurationParcel config) { + if (mTetherInternalCallback == null) return; + + try { + mTetherInternalCallback.onConfigurationChanged(config); + } catch (RemoteException e) { + // Not really very much to do here. + } + } + + private void reportTetherStateChanged(TetherStatesParcel states) { + if (mTetherInternalCallback == null) return; + + try { + mTetherInternalCallback.onTetherStatesChanged(states); + } catch (RemoteException e) { + // Not really very much to do here. + } + } + + void dump(FileDescriptor fd, PrintWriter writer, String[] args) { + // Binder.java closes the resource for us. + @SuppressWarnings("resource") + final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); + if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; + + pw.println("Tethering:"); + pw.increaseIndent(); + + pw.println("Configuration:"); + pw.increaseIndent(); + final TetheringConfiguration cfg = mConfig; + cfg.dump(pw); + pw.decreaseIndent(); + + pw.println("Entitlement:"); + pw.increaseIndent(); + mEntitlementMgr.dump(pw); + pw.decreaseIndent(); + + synchronized (mPublicSync) { + pw.println("Tether state:"); + pw.increaseIndent(); + for (int i = 0; i < mTetherStates.size(); i++) { + final String iface = mTetherStates.keyAt(i); + final TetherState tetherState = mTetherStates.valueAt(i); + pw.print(iface + " - "); + + switch (tetherState.lastState) { + case IpServer.STATE_UNAVAILABLE: + pw.print("UnavailableState"); + break; + case IpServer.STATE_AVAILABLE: + pw.print("AvailableState"); + break; + case IpServer.STATE_TETHERED: + pw.print("TetheredState"); + break; + case IpServer.STATE_LOCAL_ONLY: + pw.print("LocalHotspotState"); + break; + default: + pw.print("UnknownState"); + break; + } + pw.println(" - lastError = " + tetherState.lastError); + } + pw.println("Upstream wanted: " + upstreamWanted()); + pw.println("Current upstream interface(s): " + mCurrentUpstreamIfaceSet); + pw.decreaseIndent(); + } + + pw.println("Hardware offload:"); + pw.increaseIndent(); + mOffloadController.dump(pw); + pw.decreaseIndent(); + + pw.println("Log:"); + pw.increaseIndent(); + if (argsContain(args, "--short")) { + pw.println(""); + } else { + mLog.dump(fd, pw, args); + } + pw.decreaseIndent(); + + pw.decreaseIndent(); + } + + private static boolean argsContain(String[] args, String target) { + for (String arg : args) { + if (target.equals(arg)) return true; + } + return false; + } + + private IpServer.Callback makeControlCallback() { + return new IpServer.Callback() { + @Override + public void updateInterfaceState(IpServer who, int state, int lastError) { + notifyInterfaceStateChange(who, state, lastError); + } + + @Override + public void updateLinkProperties(IpServer who, LinkProperties newLp) { + notifyLinkPropertiesChanged(who, newLp); + } + }; + } + + // TODO: Move into TetherMasterSM. + private void notifyInterfaceStateChange(IpServer who, int state, int error) { + final String iface = who.interfaceName(); + synchronized (mPublicSync) { + final TetherState tetherState = mTetherStates.get(iface); + if (tetherState != null && tetherState.ipServer.equals(who)) { + tetherState.lastState = state; + tetherState.lastError = error; + } else { + if (DBG) Log.d(TAG, "got notification from stale iface " + iface); + } + } + + mLog.log(String.format("OBSERVED iface=%s state=%s error=%s", iface, state, error)); + + try { + // Notify that we're tethering (or not) this interface. + // This is how data saver for instance knows if the user explicitly + // turned on tethering (thus keeping us from being in data saver mode). + mPolicyManager.onTetheringChanged(iface, state == IpServer.STATE_TETHERED); + } catch (RemoteException e) { + // Not really very much we can do here. + } + + // If TetherMasterSM is in ErrorState, TetherMasterSM stays there. + // Thus we give a chance for TetherMasterSM to recover to InitialState + // by sending CMD_CLEAR_ERROR + if (error == TETHER_ERROR_MASTER_ERROR) { + mTetherMasterSM.sendMessage(TetherMasterSM.CMD_CLEAR_ERROR, who); + } + int which; + switch (state) { + case IpServer.STATE_UNAVAILABLE: + case IpServer.STATE_AVAILABLE: + which = TetherMasterSM.EVENT_IFACE_SERVING_STATE_INACTIVE; + break; + case IpServer.STATE_TETHERED: + case IpServer.STATE_LOCAL_ONLY: + which = TetherMasterSM.EVENT_IFACE_SERVING_STATE_ACTIVE; + break; + default: + Log.wtf(TAG, "Unknown interface state: " + state); + return; + } + mTetherMasterSM.sendMessage(which, state, 0, who); + sendTetherStateChangedBroadcast(); + } + + private void notifyLinkPropertiesChanged(IpServer who, LinkProperties newLp) { + final String iface = who.interfaceName(); + final int state; + synchronized (mPublicSync) { + final TetherState tetherState = mTetherStates.get(iface); + if (tetherState != null && tetherState.ipServer.equals(who)) { + state = tetherState.lastState; + } else { + mLog.log("got notification from stale iface " + iface); + return; + } + } + + mLog.log(String.format( + "OBSERVED LinkProperties update iface=%s state=%s lp=%s", + iface, IpServer.getStateString(state), newLp)); + final int which = TetherMasterSM.EVENT_IFACE_UPDATE_LINKPROPERTIES; + mTetherMasterSM.sendMessage(which, state, 0, newLp); + } + + private void maybeTrackNewInterfaceLocked(final String iface) { + // If we don't care about this type of interface, ignore. + final int interfaceType = ifaceNameToType(iface); + if (interfaceType == TETHERING_INVALID) { + mLog.log(iface + " is not a tetherable iface, ignoring"); + return; + } + maybeTrackNewInterfaceLocked(iface, interfaceType); + } + + private void maybeTrackNewInterfaceLocked(final String iface, int interfaceType) { + // If we have already started a TISM for this interface, skip. + if (mTetherStates.containsKey(iface)) { + mLog.log("active iface (" + iface + ") reported as added, ignoring"); + return; + } + + mLog.log("adding TetheringInterfaceStateMachine for: " + iface); + final TetherState tetherState = new TetherState( + new IpServer(iface, mLooper, interfaceType, mLog, mNMService, mStatsService, + makeControlCallback(), mConfig.enableLegacyDhcpServer, + mDeps.getIpServerDependencies())); + mTetherStates.put(iface, tetherState); + tetherState.ipServer.start(); + } + + private void stopTrackingInterfaceLocked(final String iface) { + final TetherState tetherState = mTetherStates.get(iface); + if (tetherState == null) { + mLog.log("attempting to remove unknown iface (" + iface + "), ignoring"); + return; + } + tetherState.ipServer.stop(); + mLog.log("removing TetheringInterfaceStateMachine for: " + iface); + mTetherStates.remove(iface); + } + + private static String[] copy(String[] strarray) { + return Arrays.copyOf(strarray, strarray.length); + } +} diff --git a/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java b/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java index ca9b1681ba..0ab4d634e8 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java +++ b/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java @@ -38,6 +38,7 @@ import android.content.ContentResolver; import android.content.Context; import android.content.res.Resources; import android.net.ConnectivityManager; +import android.net.TetheringConfigurationParcel; import android.net.util.SharedLog; import android.provider.Settings; import android.telephony.SubscriptionManager; @@ -384,4 +385,32 @@ public class TetheringConfiguration { } return false; } + + /** + * Convert this TetheringConfiguration to a TetheringConfigurationParcel. + */ + public TetheringConfigurationParcel toStableParcelable() { + final TetheringConfigurationParcel parcel = new TetheringConfigurationParcel(); + parcel.subId = subId; + parcel.tetherableUsbRegexs = tetherableUsbRegexs; + parcel.tetherableWifiRegexs = tetherableWifiRegexs; + parcel.tetherableBluetoothRegexs = tetherableBluetoothRegexs; + parcel.isDunRequired = isDunRequired; + parcel.chooseUpstreamAutomatically = chooseUpstreamAutomatically; + + int[] preferredTypes = new int[preferredUpstreamIfaceTypes.size()]; + int index = 0; + for (Integer type : preferredUpstreamIfaceTypes) { + preferredTypes[index++] = type; + } + parcel.preferredUpstreamIfaceTypes = preferredTypes; + + parcel.legacyDhcpRanges = legacyDhcpRanges; + parcel.defaultIPv4DNS = defaultIPv4DNS; + parcel.enableLegacyDhcpServer = enableLegacyDhcpServer; + parcel.provisioningApp = provisioningApp; + parcel.provisioningAppNoUi = provisioningAppNoUi; + parcel.provisioningCheckPeriod = provisioningCheckPeriod; + return parcel; + } } diff --git a/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java b/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java new file mode 100644 index 0000000000..0ba84127d8 --- /dev/null +++ b/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.connectivity.tethering; + +import android.content.Context; +import android.net.INetd; +import android.net.INetworkPolicyManager; +import android.net.INetworkStatsService; +import android.net.NetworkRequest; +import android.net.ip.IpServer; +import android.net.util.SharedLog; +import android.os.Handler; +import android.os.IBinder; +import android.os.INetworkManagementService; +import android.os.Looper; +import android.os.ServiceManager; + +import com.android.internal.util.StateMachine; + +import java.util.ArrayList; + + +/** + * Capture tethering dependencies, for injection. + * + * @hide + */ +public class TetheringDependencies { + /** + * Get a reference to the offload hardware interface to be used by tethering. + */ + public OffloadHardwareInterface getOffloadHardwareInterface(Handler h, SharedLog log) { + return new OffloadHardwareInterface(h, log); + } + + /** + * Get a reference to the UpstreamNetworkMonitor to be used by tethering. + */ + public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx, StateMachine target, + SharedLog log, int what) { + return new UpstreamNetworkMonitor(ctx, target, log, what); + } + + /** + * Get a reference to the IPv6TetheringCoordinator to be used by tethering. + */ + public IPv6TetheringCoordinator getIPv6TetheringCoordinator( + ArrayList notifyList, SharedLog log) { + return new IPv6TetheringCoordinator(notifyList, log); + } + + /** + * Get dependencies to be used by IpServer. + */ + public IpServer.Dependencies getIpServerDependencies() { + return new IpServer.Dependencies(); + } + + /** + * Indicates whether tethering is supported on the device. + */ + public boolean isTetheringSupported() { + return true; + } + + /** + * Get the NetworkRequest that should be fulfilled by the default network. + */ + public NetworkRequest getDefaultNetworkRequest() { + return null; + } + + /** + * Get a reference to the EntitlementManager to be used by tethering. + */ + public EntitlementManager getEntitlementManager(Context ctx, StateMachine target, + SharedLog log, int what) { + return new EntitlementManager(ctx, target, log, what); + } + + /** + * Generate a new TetheringConfiguration according to input sub Id. + */ + public TetheringConfiguration generateTetheringConfiguration(Context ctx, SharedLog log, + int subId) { + return new TetheringConfiguration(ctx, log, subId); + } + + /** + * Get a reference to INetworkManagementService to registerTetheringStatsProvider from + * OffloadController. Note: This should be removed soon by Usage refactor work in R + * development cycle. + */ + public INetworkManagementService getINetworkManagementService() { + return INetworkManagementService.Stub.asInterface( + ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE)); + } + + /** + * Get a reference to INetworkStatsService to force update tethering usage. + * Note: This should be removed in R development cycle. + */ + public INetworkStatsService getINetworkStatsService() { + return INetworkStatsService.Stub.asInterface( + ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); + } + + /** + * Get a reference to INetworkPolicyManager to be used by tethering. + */ + public INetworkPolicyManager getINetworkPolicyManager() { + return INetworkPolicyManager.Stub.asInterface( + ServiceManager.getService(Context.NETWORK_POLICY_SERVICE)); + } + + /** + * Get a reference to INetd to be used by tethering. + */ + public INetd getINetd(Context context) { + return INetd.Stub.asInterface( + (IBinder) context.getSystemService(Context.NETD_SERVICE)); + } + + /** + * Get tethering thread looper. + */ + public Looper getTetheringLooper() { + return null; + } + + /** + * Get Context of TetheringSerice. + */ + public Context getContext() { + return null; + } +} diff --git a/Tethering/src/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java b/Tethering/src/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java new file mode 100644 index 0000000000..0ef3805ff7 --- /dev/null +++ b/Tethering/src/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java @@ -0,0 +1,90 @@ +/* + * 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.connectivity.tethering; + +import android.annotation.Nullable; +import android.net.LinkProperties; +import android.net.NetworkCapabilities; +import android.net.NetworkState; +import android.net.RouteInfo; +import android.net.util.InterfaceSet; + +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; + +/** + * @hide + */ +public final class TetheringInterfaceUtils { + /** + * Get upstream interfaces for tethering based on default routes for IPv4/IPv6. + * @return null if there is no usable interface, or a set of at least one interface otherwise. + */ + public static @Nullable InterfaceSet getTetheringInterfaces(NetworkState ns) { + if (ns == null) { + return null; + } + + final LinkProperties lp = ns.linkProperties; + final String if4 = getInterfaceForDestination(lp, Inet4Address.ANY); + final String if6 = getIPv6Interface(ns); + + return (if4 == null && if6 == null) ? null : new InterfaceSet(if4, if6); + } + + /** + * Get the upstream interface for IPv6 tethering. + * @return null if there is no usable interface, or the interface name otherwise. + */ + public static @Nullable String getIPv6Interface(NetworkState ns) { + // Broadly speaking: + // + // [1] does the upstream have an IPv6 default route? + // + // and + // + // [2] does the upstream have one or more global IPv6 /64s + // dedicated to this device? + // + // In lieu of Prefix Delegation and other evaluation of whether a + // prefix may or may not be dedicated to this device, for now just + // check whether the upstream is TRANSPORT_CELLULAR. This works + // because "[t]he 3GPP network allocates each default bearer a unique + // /64 prefix", per RFC 6459, Section 5.2. + final boolean canTether = + (ns != null) && (ns.network != null) + && (ns.linkProperties != null) && (ns.networkCapabilities != null) + // At least one upstream DNS server: + && ns.linkProperties.hasIpv6DnsServer() + // Minimal amount of IPv6 provisioning: + && ns.linkProperties.hasGlobalIpv6Address() + // Temporary approximation of "dedicated prefix": + && ns.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR); + + return canTether + ? getInterfaceForDestination(ns.linkProperties, Inet6Address.ANY) + : null; + } + + private static String getInterfaceForDestination(LinkProperties lp, InetAddress dst) { + final RouteInfo ri = (lp != null) + ? RouteInfo.selectBestRoute(lp.getAllRoutes(), dst) + : null; + return (ri != null) ? ri.getInterface() : null; + } +} diff --git a/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java b/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java new file mode 100644 index 0000000000..456f2f7f27 --- /dev/null +++ b/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java @@ -0,0 +1,178 @@ +/* + * 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 com.android.server.connectivity.tethering; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.net.ConnectivityManager; +import android.net.ITetherInternalCallback; +import android.net.ITetheringConnector; +import android.net.NetworkRequest; +import android.net.util.SharedLog; +import android.os.HandlerThread; +import android.os.IBinder; +import android.os.Looper; +import android.os.ResultReceiver; +import android.os.SystemProperties; +import android.provider.Settings; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.internal.annotations.VisibleForTesting; + +import java.io.FileDescriptor; +import java.io.PrintWriter; + +/** + * Android service used to manage tethering. + * + *

The service returns a binder for the system server to communicate with the tethering. + */ +public class TetheringService extends Service { + private static final String TAG = TetheringService.class.getSimpleName(); + + private final SharedLog mLog = new SharedLog(TAG); + private TetheringConnector mConnector; + private Context mContext; + private TetheringDependencies mDeps; + private Tethering mTethering; + + @Override + public void onCreate() { + mLog.mark("onCreate"); + mDeps = getTetheringDependencies(); + mContext = mDeps.getContext(); + mTethering = makeTethering(mDeps); + } + + /** + * Make a reference to Tethering object. + */ + @VisibleForTesting + public Tethering makeTethering(TetheringDependencies deps) { + return new Tethering(deps); + } + + /** + * Create a binder connector for the system server to communicate with the tethering. + */ + private synchronized IBinder makeConnector() { + if (mConnector == null) { + mConnector = new TetheringConnector(mTethering); + } + return mConnector; + } + + @NonNull + @Override + public IBinder onBind(Intent intent) { + mLog.mark("onBind"); + return makeConnector(); + } + + private static class TetheringConnector extends ITetheringConnector.Stub { + private final Tethering mService; + + TetheringConnector(Tethering tether) { + mService = tether; + } + + @Override + public void tether(String iface) { + mService.tether(iface); + } + + @Override + public void untether(String iface) { + mService.untether(iface); + } + + @Override + public void setUsbTethering(boolean enable) { + mService.setUsbTethering(enable); + } + + @Override + public void startTethering(int type, ResultReceiver receiver, boolean showProvisioningUi) { + mService.startTethering(type, receiver, showProvisioningUi); + } + + @Override + public void stopTethering(int type) { + mService.stopTethering(type); + } + + @Override + public void requestLatestTetheringEntitlementResult(int type, ResultReceiver receiver, + boolean showEntitlementUi) { + mService.requestLatestTetheringEntitlementResult(type, receiver, showEntitlementUi); + } + + @Override + public void registerTetherInternalCallback(ITetherInternalCallback callback) { + mService.registerTetherInternalCallback(callback); + } + } + + @Override + protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer, + @Nullable String[] args) { + mTethering.dump(fd, writer, args); + } + + /** + * An injection method for testing. + */ + @VisibleForTesting + public TetheringDependencies getTetheringDependencies() { + if (mDeps == null) { + mDeps = new TetheringDependencies() { + @Override + public NetworkRequest getDefaultNetworkRequest() { + ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService( + Context.CONNECTIVITY_SERVICE); + return cm.getDefaultRequest(); + } + + @Override + public Looper getTetheringLooper() { + final HandlerThread tetherThread = new HandlerThread("android.tethering"); + tetherThread.start(); + return tetherThread.getLooper(); + } + + @Override + public boolean isTetheringSupported() { + int defaultVal = + SystemProperties.get("ro.tether.denied").equals("true") ? 0 : 1; + boolean tetherSupported = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.TETHER_SUPPORTED, defaultVal) != 0; + return tetherSupported; + } + + @Override + public Context getContext() { + return TetheringService.this; + } + }; + } + + return mDeps; + } +} diff --git a/Tethering/tests/unit/Android.bp b/Tethering/tests/unit/Android.bp index 363be18a73..5b018df21a 100644 --- a/Tethering/tests/unit/Android.bp +++ b/Tethering/tests/unit/Android.bp @@ -18,7 +18,6 @@ android_test { name: "TetheringTests", certificate: "platform", srcs: [ - ":servicescore-tethering-src", "src/**/*.java", ], test_suites: ["device-tests"], @@ -41,17 +40,3 @@ android_test { "libstaticjvmtiagent", ], } - -// This group would be removed when tethering migration is done. -filegroup { - name: "tethering-tests-src", - srcs: [ - "src/com/android/server/connectivity/tethering/EntitlementManagerTest.java", - "src/com/android/server/connectivity/tethering/OffloadControllerTest.java", - "src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java", - "src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java", - "src/android/net/dhcp/DhcpServingParamsParcelExtTest.java", - "src/android/net/ip/IpServerTest.java", - "src/android/net/util/InterfaceSetTest.java", - ], -} diff --git a/Tethering/tests/unit/src/android/net/util/VersionedBroadcastListenerTest.java b/Tethering/tests/unit/src/android/net/util/VersionedBroadcastListenerTest.java new file mode 100644 index 0000000000..5a9b6e380e --- /dev/null +++ b/Tethering/tests/unit/src/android/net/util/VersionedBroadcastListenerTest.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2017 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.util; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.reset; + +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Handler; +import android.os.Looper; +import android.os.UserHandle; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.util.test.BroadcastInterceptingContext; + +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class VersionedBroadcastListenerTest { + private static final String TAG = VersionedBroadcastListenerTest.class.getSimpleName(); + private static final String ACTION_TEST = "action.test.happy.broadcasts"; + + @Mock private Context mContext; + private BroadcastInterceptingContext mServiceContext; + private Handler mHandler; + private VersionedBroadcastListener mListener; + private int mCallbackCount; + + private void doCallback() { + mCallbackCount++; + } + + private class MockContext extends BroadcastInterceptingContext { + MockContext(Context base) { + super(base); + } + } + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + if (Looper.myLooper() == null) { + Looper.prepare(); + } + } + + @Before public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + reset(mContext); + mServiceContext = new MockContext(mContext); + mHandler = new Handler(Looper.myLooper()); + mCallbackCount = 0; + final IntentFilter filter = new IntentFilter(); + filter.addAction(ACTION_TEST); + mListener = new VersionedBroadcastListener( + TAG, mServiceContext, mHandler, filter, (Intent intent) -> doCallback()); + } + + @After public void tearDown() throws Exception { + if (mListener != null) { + mListener.stopListening(); + mListener = null; + } + } + + private void sendBroadcast() { + final Intent intent = new Intent(ACTION_TEST); + mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); + } + + @Test + public void testBasicListening() { + assertEquals(0, mCallbackCount); + mListener.startListening(); + for (int i = 0; i < 5; i++) { + sendBroadcast(); + assertEquals(i + 1, mCallbackCount); + } + mListener.stopListening(); + } + + @Test + public void testBroadcastsBeforeStartAreIgnored() { + assertEquals(0, mCallbackCount); + for (int i = 0; i < 5; i++) { + sendBroadcast(); + assertEquals(0, mCallbackCount); + } + + mListener.startListening(); + sendBroadcast(); + assertEquals(1, mCallbackCount); + } + + @Test + public void testBroadcastsAfterStopAreIgnored() { + mListener.startListening(); + sendBroadcast(); + assertEquals(1, mCallbackCount); + mListener.stopListening(); + + for (int i = 0; i < 5; i++) { + sendBroadcast(); + assertEquals(1, mCallbackCount); + } + } +} diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java index 5217e26a40..99cf9e90d9 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java +++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java @@ -24,6 +24,9 @@ import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR; import static android.net.ConnectivityManager.TETHER_ERROR_PROVISION_FAILED; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -44,6 +47,7 @@ import android.os.Bundle; import android.os.Message; import android.os.PersistableBundle; import android.os.ResultReceiver; +import android.os.SystemProperties; import android.os.test.TestLooper; import android.provider.Settings; import android.telephony.CarrierConfigManager; @@ -57,7 +61,6 @@ import com.android.internal.util.State; import com.android.internal.util.StateMachine; import com.android.internal.util.test.BroadcastInterceptingContext; import com.android.internal.util.test.FakeSettingsProvider; -import com.android.server.connectivity.MockableSystemProperties; import org.junit.After; import org.junit.Before; @@ -65,6 +68,8 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.MockitoSession; +import org.mockito.quality.Strictness; import java.util.ArrayList; import java.util.concurrent.CountDownLatch; @@ -80,7 +85,6 @@ public final class EntitlementManagerTest { @Mock private CarrierConfigManager mCarrierConfigManager; @Mock private Context mContext; - @Mock private MockableSystemProperties mSystemProperties; @Mock private Resources mResources; @Mock private SharedLog mLog; @Mock private EntitlementManager.OnUiEntitlementFailedListener mEntitlementFailedListener; @@ -95,6 +99,7 @@ public final class EntitlementManagerTest { private TestStateMachine mSM; private WrappedEntitlementManager mEnMgr; private TetheringConfiguration mConfig; + private MockitoSession mMockingSession; private class MockContext extends BroadcastInterceptingContext { MockContext(Context base) { @@ -118,8 +123,8 @@ public final class EntitlementManagerTest { public int silentProvisionCount = 0; public WrappedEntitlementManager(Context ctx, StateMachine target, - SharedLog log, int what, MockableSystemProperties systemProperties) { - super(ctx, target, log, what, systemProperties); + SharedLog log, int what) { + super(ctx, target, log, what); } public void reset() { @@ -144,6 +149,15 @@ public final class EntitlementManagerTest { @Before public void setUp() { MockitoAnnotations.initMocks(this); + mMockingSession = mockitoSession() + .initMocks(this) + .spyStatic(SystemProperties.class) + .strictness(Strictness.WARN) + .startMocking(); + // Don't disable tethering provisioning unless requested. + doReturn(false).when( + () -> SystemProperties.getBoolean( + eq(EntitlementManager.DISABLE_PROVISIONING_SYSPROP_KEY), anyBoolean())); when(mResources.getStringArray(R.array.config_tether_dhcp_range)) .thenReturn(new String[0]); @@ -161,8 +175,7 @@ public final class EntitlementManagerTest { mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); mMockContext = new MockContext(mContext); mSM = new TestStateMachine(); - mEnMgr = new WrappedEntitlementManager(mMockContext, mSM, mLog, EVENT_EM_UPDATE, - mSystemProperties); + mEnMgr = new WrappedEntitlementManager(mMockContext, mSM, mLog, EVENT_EM_UPDATE); mEnMgr.setOnUiEntitlementFailedListener(mEntitlementFailedListener); mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); mEnMgr.setTetheringConfigurationFetcher(() -> { @@ -176,6 +189,7 @@ public final class EntitlementManagerTest { mSM.quit(); mSM = null; } + mMockingSession.finishMocking(); } private void setupForRequiredProvisioning() { @@ -184,9 +198,6 @@ public final class EntitlementManagerTest { .thenReturn(PROVISIONING_APP_NAME); when(mResources.getString(R.string.config_mobile_hotspot_provision_app_no_ui)) .thenReturn(PROVISIONING_NO_UI_APP_NAME); - // Don't disable tethering provisioning unless requested. - when(mSystemProperties.getBoolean(eq(EntitlementManager.DISABLE_PROVISIONING_SYSPROP_KEY), - anyBoolean())).thenReturn(false); // Act like the CarrierConfigManager is present and ready unless told otherwise. when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE)) .thenReturn(mCarrierConfigManager); @@ -244,7 +255,7 @@ public final class EntitlementManagerTest { } @Test - public void testGetLastEntitlementCacheValue() throws Exception { + public void testRequestLastEntitlementCacheValue() throws Exception { final CountDownLatch mCallbacklatch = new CountDownLatch(1); // 1. Entitlement check is not required. mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; @@ -255,7 +266,7 @@ public final class EntitlementManagerTest { mCallbacklatch.countDown(); } }; - mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true); + mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true); mLooper.dispatchAll(); callbackTimeoutHelper(mCallbacklatch); assertEquals(0, mEnMgr.uiProvisionCount); @@ -270,7 +281,7 @@ public final class EntitlementManagerTest { mCallbacklatch.countDown(); } }; - mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false); + mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false); mLooper.dispatchAll(); callbackTimeoutHelper(mCallbacklatch); assertEquals(0, mEnMgr.uiProvisionCount); @@ -284,7 +295,7 @@ public final class EntitlementManagerTest { mCallbacklatch.countDown(); } }; - mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true); + mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true); mLooper.dispatchAll(); callbackTimeoutHelper(mCallbacklatch); assertEquals(1, mEnMgr.uiProvisionCount); @@ -298,7 +309,7 @@ public final class EntitlementManagerTest { mCallbacklatch.countDown(); } }; - mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false); + mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false); mLooper.dispatchAll(); callbackTimeoutHelper(mCallbacklatch); assertEquals(0, mEnMgr.uiProvisionCount); @@ -312,7 +323,7 @@ public final class EntitlementManagerTest { mCallbacklatch.countDown(); } }; - mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true); + mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true); mLooper.dispatchAll(); callbackTimeoutHelper(mCallbacklatch); assertEquals(1, mEnMgr.uiProvisionCount); @@ -326,7 +337,7 @@ public final class EntitlementManagerTest { mCallbacklatch.countDown(); } }; - mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true); + mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true); mLooper.dispatchAll(); callbackTimeoutHelper(mCallbacklatch); assertEquals(0, mEnMgr.uiProvisionCount); @@ -339,7 +350,7 @@ public final class EntitlementManagerTest { mCallbacklatch.countDown(); } }; - mEnMgr.getLatestTetheringEntitlementResult(TETHERING_USB, receiver, false); + mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_USB, receiver, false); mLooper.dispatchAll(); callbackTimeoutHelper(mCallbacklatch); assertEquals(0, mEnMgr.uiProvisionCount); diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java new file mode 100644 index 0000000000..4f6f61bf95 --- /dev/null +++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java @@ -0,0 +1,1319 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.connectivity.tethering; + +import static android.hardware.usb.UsbManager.USB_CONFIGURED; +import static android.hardware.usb.UsbManager.USB_CONNECTED; +import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS; +import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED; +import static android.net.ConnectivityManager.EXTRA_ACTIVE_LOCAL_ONLY; +import static android.net.ConnectivityManager.EXTRA_ACTIVE_TETHER; +import static android.net.ConnectivityManager.EXTRA_AVAILABLE_TETHER; +import static android.net.ConnectivityManager.TETHERING_USB; +import static android.net.ConnectivityManager.TETHERING_WIFI; +import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR; +import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE; +import static android.net.ConnectivityManager.TYPE_MOBILE; +import static android.net.ConnectivityManager.TYPE_WIFI_P2P; +import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS; +import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME; +import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE; +import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE; +import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY; +import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED; +import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED; +import static android.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER; +import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.notNull; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import android.content.BroadcastReceiver; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ApplicationInfo; +import android.content.res.Resources; +import android.hardware.usb.UsbManager; +import android.net.INetd; +import android.net.INetworkPolicyManager; +import android.net.INetworkStatsService; +import android.net.ITetherInternalCallback; +import android.net.InterfaceConfiguration; +import android.net.IpPrefix; +import android.net.LinkAddress; +import android.net.LinkProperties; +import android.net.MacAddress; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.NetworkInfo; +import android.net.NetworkState; +import android.net.NetworkUtils; +import android.net.RouteInfo; +import android.net.TetherStatesParcel; +import android.net.TetheringConfigurationParcel; +import android.net.dhcp.DhcpServerCallbacks; +import android.net.dhcp.DhcpServingParamsParcel; +import android.net.dhcp.IDhcpServer; +import android.net.ip.IpServer; +import android.net.ip.RouterAdvertisementDaemon; +import android.net.util.InterfaceParams; +import android.net.util.NetworkConstants; +import android.net.util.SharedLog; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiManager; +import android.net.wifi.p2p.WifiP2pGroup; +import android.net.wifi.p2p.WifiP2pInfo; +import android.net.wifi.p2p.WifiP2pManager; +import android.os.Bundle; +import android.os.Handler; +import android.os.INetworkManagementService; +import android.os.Looper; +import android.os.PersistableBundle; +import android.os.RemoteException; +import android.os.UserHandle; +import android.os.UserManager; +import android.os.test.TestLooper; +import android.provider.Settings; +import android.telephony.CarrierConfigManager; +import android.telephony.PhoneStateListener; +import android.telephony.TelephonyManager; +import android.test.mock.MockContentResolver; + +import androidx.annotation.NonNull; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.util.ArrayUtils; +import com.android.internal.util.StateMachine; +import com.android.internal.util.test.BroadcastInterceptingContext; +import com.android.internal.util.test.FakeSettingsProvider; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Vector; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class TetheringTest { + private static final int IFINDEX_OFFSET = 100; + + private static final String TEST_MOBILE_IFNAME = "test_rmnet_data0"; + private static final String TEST_XLAT_MOBILE_IFNAME = "v4-test_rmnet_data0"; + private static final String TEST_USB_IFNAME = "test_rndis0"; + private static final String TEST_WLAN_IFNAME = "test_wlan0"; + private static final String TEST_P2P_IFNAME = "test_p2p-p2p0-0"; + + private static final int DHCPSERVER_START_TIMEOUT_MS = 1000; + + @Mock private ApplicationInfo mApplicationInfo; + @Mock private Context mContext; + @Mock private INetworkManagementService mNMService; + @Mock private INetworkStatsService mStatsService; + @Mock private INetworkPolicyManager mPolicyManager; + @Mock private OffloadHardwareInterface mOffloadHardwareInterface; + @Mock private Resources mResources; + @Mock private TelephonyManager mTelephonyManager; + @Mock private UsbManager mUsbManager; + @Mock private WifiManager mWifiManager; + @Mock private CarrierConfigManager mCarrierConfigManager; + @Mock private UpstreamNetworkMonitor mUpstreamNetworkMonitor; + @Mock private IPv6TetheringCoordinator mIPv6TetheringCoordinator; + @Mock private RouterAdvertisementDaemon mRouterAdvertisementDaemon; + @Mock private IDhcpServer mDhcpServer; + @Mock private INetd mNetd; + + private final MockIpServerDependencies mIpServerDependencies = + spy(new MockIpServerDependencies()); + private final MockTetheringDependencies mTetheringDependencies = + new MockTetheringDependencies(); + + // Like so many Android system APIs, these cannot be mocked because it is marked final. + // We have to use the real versions. + private final PersistableBundle mCarrierConfig = new PersistableBundle(); + private final TestLooper mLooper = new TestLooper(); + + private Vector mIntents; + private BroadcastInterceptingContext mServiceContext; + private MockContentResolver mContentResolver; + private BroadcastReceiver mBroadcastReceiver; + private Tethering mTethering; + private PhoneStateListener mPhoneStateListener; + + private class TestContext extends BroadcastInterceptingContext { + TestContext(Context base) { + super(base); + } + + @Override + public ApplicationInfo getApplicationInfo() { + return mApplicationInfo; + } + + @Override + public ContentResolver getContentResolver() { + return mContentResolver; + } + + @Override + public String getPackageName() { + return "TetheringTest"; + } + + @Override + public Resources getResources() { + return mResources; + } + + @Override + public Object getSystemService(String name) { + if (Context.WIFI_SERVICE.equals(name)) return mWifiManager; + if (Context.USB_SERVICE.equals(name)) return mUsbManager; + if (Context.TELEPHONY_SERVICE.equals(name)) return mTelephonyManager; + return super.getSystemService(name); + } + + @Override + public String getSystemServiceName(Class serviceClass) { + if (TelephonyManager.class.equals(serviceClass)) return Context.TELEPHONY_SERVICE; + return super.getSystemServiceName(serviceClass); + } + } + + public class MockIpServerDependencies extends IpServer.Dependencies { + @Override + public RouterAdvertisementDaemon getRouterAdvertisementDaemon( + InterfaceParams ifParams) { + return mRouterAdvertisementDaemon; + } + + @Override + public InterfaceParams getInterfaceParams(String ifName) { + assertTrue("Non-mocked interface " + ifName, + ifName.equals(TEST_USB_IFNAME) + || ifName.equals(TEST_WLAN_IFNAME) + || ifName.equals(TEST_MOBILE_IFNAME) + || ifName.equals(TEST_P2P_IFNAME)); + final String[] ifaces = new String[] { + TEST_USB_IFNAME, TEST_WLAN_IFNAME, TEST_MOBILE_IFNAME, TEST_P2P_IFNAME}; + return new InterfaceParams(ifName, ArrayUtils.indexOf(ifaces, ifName) + IFINDEX_OFFSET, + MacAddress.ALL_ZEROS_ADDRESS); + } + + @Override + public INetd getNetdService() { + return mNetd; + } + + @Override + public void makeDhcpServer(String ifName, DhcpServingParamsParcel params, + DhcpServerCallbacks cb) { + new Thread(() -> { + try { + cb.onDhcpServerCreated(STATUS_SUCCESS, mDhcpServer); + } catch (RemoteException e) { + fail(e.getMessage()); + } + }).run(); + } + } + + private class MockTetheringConfiguration extends TetheringConfiguration { + MockTetheringConfiguration(Context ctx, SharedLog log, int id) { + super(ctx, log, id); + } + + @Override + protected Resources getResourcesForSubIdWrapper(Context ctx, int subId) { + return mResources; + } + } + + public class MockTetheringDependencies extends TetheringDependencies { + StateMachine mUpstreamNetworkMonitorMasterSM; + ArrayList mIpv6CoordinatorNotifyList; + int mIsTetheringSupportedCalls; + + public void reset() { + mUpstreamNetworkMonitorMasterSM = null; + mIpv6CoordinatorNotifyList = null; + mIsTetheringSupportedCalls = 0; + } + + @Override + public OffloadHardwareInterface getOffloadHardwareInterface(Handler h, SharedLog log) { + return mOffloadHardwareInterface; + } + + @Override + public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx, + StateMachine target, SharedLog log, int what) { + mUpstreamNetworkMonitorMasterSM = target; + return mUpstreamNetworkMonitor; + } + + @Override + public IPv6TetheringCoordinator getIPv6TetheringCoordinator( + ArrayList notifyList, SharedLog log) { + mIpv6CoordinatorNotifyList = notifyList; + return mIPv6TetheringCoordinator; + } + + @Override + public IpServer.Dependencies getIpServerDependencies() { + return mIpServerDependencies; + } + + @Override + public boolean isTetheringSupported() { + mIsTetheringSupportedCalls++; + return true; + } + + @Override + public TetheringConfiguration generateTetheringConfiguration(Context ctx, SharedLog log, + int subId) { + return new MockTetheringConfiguration(ctx, log, subId); + } + + @Override + public INetworkManagementService getINetworkManagementService() { + return mNMService; + } + + @Override + public INetworkStatsService getINetworkStatsService() { + return mStatsService; + } + + @Override + public INetworkPolicyManager getINetworkPolicyManager() { + return mPolicyManager; + } + + @Override + public INetd getINetd(Context context) { + return mNetd; + } + + @Override + public Looper getTetheringLooper() { + return mLooper.getLooper(); + } + + @Override + public Context getContext() { + return mServiceContext; + } + } + + private static NetworkState buildMobileUpstreamState(boolean withIPv4, boolean withIPv6, + boolean with464xlat) { + final NetworkInfo info = new NetworkInfo(TYPE_MOBILE, 0, null, null); + info.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null); + final LinkProperties prop = new LinkProperties(); + prop.setInterfaceName(TEST_MOBILE_IFNAME); + + if (withIPv4) { + prop.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), + NetworkUtils.numericToInetAddress("10.0.0.1"), TEST_MOBILE_IFNAME)); + } + + if (withIPv6) { + prop.addDnsServer(NetworkUtils.numericToInetAddress("2001:db8::2")); + prop.addLinkAddress( + new LinkAddress(NetworkUtils.numericToInetAddress("2001:db8::"), + NetworkConstants.RFC7421_PREFIX_LENGTH)); + prop.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), + NetworkUtils.numericToInetAddress("2001:db8::1"), TEST_MOBILE_IFNAME)); + } + + if (with464xlat) { + final LinkProperties stackedLink = new LinkProperties(); + stackedLink.setInterfaceName(TEST_XLAT_MOBILE_IFNAME); + stackedLink.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), + NetworkUtils.numericToInetAddress("192.0.0.1"), TEST_XLAT_MOBILE_IFNAME)); + + prop.addStackedLink(stackedLink); + } + + + final NetworkCapabilities capabilities = new NetworkCapabilities() + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); + return new NetworkState(info, prop, capabilities, new Network(100), null, "netid"); + } + + private static NetworkState buildMobileIPv4UpstreamState() { + return buildMobileUpstreamState(true, false, false); + } + + private static NetworkState buildMobileIPv6UpstreamState() { + return buildMobileUpstreamState(false, true, false); + } + + private static NetworkState buildMobileDualStackUpstreamState() { + return buildMobileUpstreamState(true, true, false); + } + + private static NetworkState buildMobile464xlatUpstreamState() { + return buildMobileUpstreamState(false, true, true); + } + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + when(mResources.getStringArray(com.android.internal.R.array.config_tether_dhcp_range)) + .thenReturn(new String[0]); + when(mResources.getStringArray(com.android.internal.R.array.config_tether_usb_regexs)) + .thenReturn(new String[] { "test_rndis\\d" }); + when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_regexs)) + .thenReturn(new String[]{ "test_wlan\\d" }); + when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_p2p_regexs)) + .thenReturn(new String[]{ "test_p2p-p2p\\d-.*" }); + when(mResources.getStringArray(com.android.internal.R.array.config_tether_bluetooth_regexs)) + .thenReturn(new String[0]); + when(mResources.getIntArray(com.android.internal.R.array.config_tether_upstream_types)) + .thenReturn(new int[0]); + when(mResources.getBoolean(com.android.internal.R.bool.config_tether_upstream_automatic)) + .thenReturn(false); + when(mNMService.listInterfaces()) + .thenReturn(new String[] { + TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME}); + when(mNMService.getInterfaceConfig(anyString())) + .thenReturn(new InterfaceConfiguration()); + when(mRouterAdvertisementDaemon.start()) + .thenReturn(true); + + mServiceContext = new TestContext(mContext); + mContentResolver = new MockContentResolver(mServiceContext); + mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); + Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 0); + mIntents = new Vector<>(); + mBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + mIntents.addElement(intent); + } + }; + mServiceContext.registerReceiver(mBroadcastReceiver, + new IntentFilter(ACTION_TETHER_STATE_CHANGED)); + mTethering = makeTethering(); + verify(mNMService).registerTetheringStatsProvider(any(), anyString()); + verify(mNetd).registerUnsolicitedEventListener(any()); + final ArgumentCaptor phoneListenerCaptor = + ArgumentCaptor.forClass(PhoneStateListener.class); + verify(mTelephonyManager).listen(phoneListenerCaptor.capture(), + eq(PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE)); + mPhoneStateListener = phoneListenerCaptor.getValue(); + } + + private Tethering makeTethering() { + mTetheringDependencies.reset(); + return new Tethering(mTetheringDependencies); + } + + @After + public void tearDown() { + mServiceContext.unregisterReceiver(mBroadcastReceiver); + } + + private void sendWifiApStateChanged(int state) { + final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); + intent.putExtra(EXTRA_WIFI_AP_STATE, state); + mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); + } + + private void sendWifiApStateChanged(int state, String ifname, int ipmode) { + final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); + intent.putExtra(EXTRA_WIFI_AP_STATE, state); + intent.putExtra(EXTRA_WIFI_AP_INTERFACE_NAME, ifname); + intent.putExtra(EXTRA_WIFI_AP_MODE, ipmode); + mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); + } + + private static final String[] P2P_RECEIVER_PERMISSIONS_FOR_BROADCAST = { + android.Manifest.permission.ACCESS_FINE_LOCATION, + android.Manifest.permission.ACCESS_WIFI_STATE + }; + + private void sendWifiP2pConnectionChanged( + boolean isGroupFormed, boolean isGroupOwner, String ifname) { + WifiP2pInfo p2pInfo = new WifiP2pInfo(); + p2pInfo.groupFormed = isGroupFormed; + p2pInfo.isGroupOwner = isGroupOwner; + + NetworkInfo networkInfo = new NetworkInfo(TYPE_WIFI_P2P, 0, null, null); + + WifiP2pGroup group = new WifiP2pGroup(); + group.setIsGroupOwner(isGroupOwner); + group.setInterface(ifname); + + final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION); + intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO, p2pInfo); + intent.putExtra(WifiP2pManager.EXTRA_NETWORK_INFO, networkInfo); + intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP, group); + mServiceContext.sendBroadcastAsUserMultiplePermissions(intent, UserHandle.ALL, + P2P_RECEIVER_PERMISSIONS_FOR_BROADCAST); + } + + private void sendUsbBroadcast(boolean connected, boolean configured, boolean rndisFunction) { + final Intent intent = new Intent(UsbManager.ACTION_USB_STATE); + intent.putExtra(USB_CONNECTED, connected); + intent.putExtra(USB_CONFIGURED, configured); + intent.putExtra(USB_FUNCTION_RNDIS, rndisFunction); + mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); + } + + private void sendConfigurationChanged() { + final Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED); + mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); + } + + private void verifyInterfaceServingModeStarted(String ifname) throws Exception { + verify(mNMService, times(1)).getInterfaceConfig(ifname); + verify(mNMService, times(1)) + .setInterfaceConfig(eq(ifname), any(InterfaceConfiguration.class)); + verify(mNMService, times(1)).tetherInterface(ifname); + } + + private void verifyTetheringBroadcast(String ifname, String whichExtra) { + // Verify that ifname is in the whichExtra array of the tether state changed broadcast. + final Intent bcast = mIntents.get(0); + assertEquals(ACTION_TETHER_STATE_CHANGED, bcast.getAction()); + final ArrayList ifnames = bcast.getStringArrayListExtra(whichExtra); + assertTrue(ifnames.contains(ifname)); + mIntents.remove(bcast); + } + + public void failingLocalOnlyHotspotLegacyApBroadcast( + boolean emulateInterfaceStatusChanged) throws Exception { + // Emulate externally-visible WifiManager effects, causing the + // per-interface state machine to start up, and telling us that + // hotspot mode is to be started. + if (emulateInterfaceStatusChanged) { + mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true); + } + sendWifiApStateChanged(WIFI_AP_STATE_ENABLED); + mLooper.dispatchAll(); + + // If, and only if, Tethering received an interface status changed then + // it creates a IpServer and sends out a broadcast indicating that the + // interface is "available". + if (emulateInterfaceStatusChanged) { + assertEquals(1, mTetheringDependencies.mIsTetheringSupportedCalls); + verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER); + verify(mWifiManager).updateInterfaceIpState( + TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED); + } + verifyNoMoreInteractions(mNMService); + verifyNoMoreInteractions(mWifiManager); + } + + private void prepareUsbTethering(NetworkState upstreamState) { + when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState); + when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())) + .thenReturn(upstreamState); + + // Emulate pressing the USB tethering button in Settings UI. + mTethering.startTethering(TETHERING_USB, null, false); + mLooper.dispatchAll(); + verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS); + + mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true); + } + + @Test + public void testUsbConfiguredBroadcastStartsTethering() throws Exception { + NetworkState upstreamState = buildMobileIPv4UpstreamState(); + prepareUsbTethering(upstreamState); + + // This should produce no activity of any kind. + verifyNoMoreInteractions(mNMService); + + // Pretend we then receive USB configured broadcast. + sendUsbBroadcast(true, true, true); + mLooper.dispatchAll(); + // Now we should see the start of tethering mechanics (in this case: + // tetherMatchingInterfaces() which starts by fetching all interfaces). + verify(mNMService, times(1)).listInterfaces(); + + // UpstreamNetworkMonitor should receive selected upstream + verify(mUpstreamNetworkMonitor, times(1)).selectPreferredUpstreamType(any()); + verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(upstreamState.network); + } + + @Test + public void failingLocalOnlyHotspotLegacyApBroadcastWithIfaceStatusChanged() throws Exception { + failingLocalOnlyHotspotLegacyApBroadcast(true); + } + + @Test + public void failingLocalOnlyHotspotLegacyApBroadcastSansIfaceStatusChanged() throws Exception { + failingLocalOnlyHotspotLegacyApBroadcast(false); + } + + public void workingLocalOnlyHotspotEnrichedApBroadcast( + boolean emulateInterfaceStatusChanged) throws Exception { + // Emulate externally-visible WifiManager effects, causing the + // per-interface state machine to start up, and telling us that + // hotspot mode is to be started. + if (emulateInterfaceStatusChanged) { + mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true); + } + sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_LOCAL_ONLY); + mLooper.dispatchAll(); + + verifyInterfaceServingModeStarted(TEST_WLAN_IFNAME); + verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER); + verify(mNMService, times(1)).setIpForwardingEnabled(true); + verify(mNMService, times(1)).startTethering(any(String[].class)); + verifyNoMoreInteractions(mNMService); + verify(mWifiManager).updateInterfaceIpState( + TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED); + verify(mWifiManager).updateInterfaceIpState( + TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_LOCAL_ONLY); + verifyNoMoreInteractions(mWifiManager); + verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_LOCAL_ONLY); + verify(mUpstreamNetworkMonitor, times(1)).startObserveAllNetworks(); + // This will be called twice, one is on entering IpServer.STATE_AVAILABLE, + // and another one is on IpServer.STATE_TETHERED/IpServer.STATE_LOCAL_ONLY. + assertEquals(2, mTetheringDependencies.mIsTetheringSupportedCalls); + + // Emulate externally-visible WifiManager effects, when hotspot mode + // is being torn down. + sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED); + mTethering.interfaceRemoved(TEST_WLAN_IFNAME); + mLooper.dispatchAll(); + + verify(mNMService, times(1)).untetherInterface(TEST_WLAN_IFNAME); + // {g,s}etInterfaceConfig() called twice for enabling and disabling IPv4. + verify(mNMService, times(2)).getInterfaceConfig(TEST_WLAN_IFNAME); + verify(mNMService, times(2)) + .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class)); + verify(mNMService, times(1)).stopTethering(); + verify(mNMService, times(1)).setIpForwardingEnabled(false); + verify(mWifiManager, times(3)).updateInterfaceIpState( + TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED); + verifyNoMoreInteractions(mNMService); + verifyNoMoreInteractions(mWifiManager); + // Asking for the last error after the per-interface state machine + // has been reaped yields an unknown interface error. + assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_WLAN_IFNAME)); + } + + /** + * Send CMD_IPV6_TETHER_UPDATE to IpServers as would be done by IPv6TetheringCoordinator. + */ + private void sendIPv6TetherUpdates(NetworkState upstreamState) { + // IPv6TetheringCoordinator must have been notified of downstream + verify(mIPv6TetheringCoordinator, times(1)).addActiveDownstream( + argThat(sm -> sm.linkProperties().getInterfaceName().equals(TEST_USB_IFNAME)), + eq(IpServer.STATE_TETHERED)); + + for (IpServer ipSrv : mTetheringDependencies.mIpv6CoordinatorNotifyList) { + NetworkState ipv6OnlyState = buildMobileUpstreamState(false, true, false); + ipSrv.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, + upstreamState.linkProperties.isIpv6Provisioned() + ? ipv6OnlyState.linkProperties + : null); + } + mLooper.dispatchAll(); + } + + private void runUsbTethering(NetworkState upstreamState) { + prepareUsbTethering(upstreamState); + sendUsbBroadcast(true, true, true); + mLooper.dispatchAll(); + } + + @Test + public void workingMobileUsbTethering_IPv4() throws Exception { + NetworkState upstreamState = buildMobileIPv4UpstreamState(); + runUsbTethering(upstreamState); + + verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + + sendIPv6TetherUpdates(upstreamState); + verify(mRouterAdvertisementDaemon, never()).buildNewRa(any(), notNull()); + verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any()); + } + + @Test + public void workingMobileUsbTethering_IPv4LegacyDhcp() { + Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 1); + sendConfigurationChanged(); + final NetworkState upstreamState = buildMobileIPv4UpstreamState(); + runUsbTethering(upstreamState); + sendIPv6TetherUpdates(upstreamState); + + verify(mIpServerDependencies, never()).makeDhcpServer(any(), any(), any()); + } + + @Test + public void workingMobileUsbTethering_IPv6() throws Exception { + NetworkState upstreamState = buildMobileIPv6UpstreamState(); + runUsbTethering(upstreamState); + + verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + + sendIPv6TetherUpdates(upstreamState); + verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull()); + verify(mNetd, times(1)).tetherApplyDnsInterfaces(); + } + + @Test + public void workingMobileUsbTethering_DualStack() throws Exception { + NetworkState upstreamState = buildMobileDualStackUpstreamState(); + runUsbTethering(upstreamState); + + verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mRouterAdvertisementDaemon, times(1)).start(); + verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any()); + + sendIPv6TetherUpdates(upstreamState); + verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull()); + verify(mNetd, times(1)).tetherApplyDnsInterfaces(); + } + + @Test + public void workingMobileUsbTethering_MultipleUpstreams() throws Exception { + NetworkState upstreamState = buildMobile464xlatUpstreamState(); + runUsbTethering(upstreamState); + + verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME); + verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any()); + verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, + TEST_XLAT_MOBILE_IFNAME); + + sendIPv6TetherUpdates(upstreamState); + verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull()); + verify(mNetd, times(1)).tetherApplyDnsInterfaces(); + } + + @Test + public void workingMobileUsbTethering_v6Then464xlat() throws Exception { + // Setup IPv6 + NetworkState upstreamState = buildMobileIPv6UpstreamState(); + runUsbTethering(upstreamState); + + verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any()); + verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + + // Then 464xlat comes up + upstreamState = buildMobile464xlatUpstreamState(); + when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())) + .thenReturn(upstreamState); + + // Upstream LinkProperties changed: UpstreamNetworkMonitor sends EVENT_ON_LINKPROPERTIES. + mTetheringDependencies.mUpstreamNetworkMonitorMasterSM.sendMessage( + Tethering.TetherMasterSM.EVENT_UPSTREAM_CALLBACK, + UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES, + 0, + upstreamState); + mLooper.dispatchAll(); + + // Forwarding is added for 464xlat + verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME); + verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, + TEST_XLAT_MOBILE_IFNAME); + // Forwarding was not re-added for v6 (still times(1)) + verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + // DHCP not restarted on downstream (still times(1)) + verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any()); + } + + @Test + public void configTetherUpstreamAutomaticIgnoresConfigTetherUpstreamTypes() throws Exception { + when(mResources.getBoolean(com.android.internal.R.bool.config_tether_upstream_automatic)) + .thenReturn(true); + sendConfigurationChanged(); + + // Setup IPv6 + final NetworkState upstreamState = buildMobileIPv6UpstreamState(); + runUsbTethering(upstreamState); + + // UpstreamNetworkMonitor should choose upstream automatically + // (in this specific case: choose the default network). + verify(mUpstreamNetworkMonitor, times(1)).getCurrentPreferredUpstream(); + verify(mUpstreamNetworkMonitor, never()).selectPreferredUpstreamType(any()); + + verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(upstreamState.network); + } + + @Test + public void workingLocalOnlyHotspotEnrichedApBroadcastWithIfaceChanged() throws Exception { + workingLocalOnlyHotspotEnrichedApBroadcast(true); + } + + @Test + public void workingLocalOnlyHotspotEnrichedApBroadcastSansIfaceChanged() throws Exception { + workingLocalOnlyHotspotEnrichedApBroadcast(false); + } + + // TODO: Test with and without interfaceStatusChanged(). + @Test + public void failingWifiTetheringLegacyApBroadcast() throws Exception { + when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true); + + // Emulate pressing the WiFi tethering button. + mTethering.startTethering(TETHERING_WIFI, null, false); + mLooper.dispatchAll(); + verify(mWifiManager, times(1)).startSoftAp(null); + verifyNoMoreInteractions(mWifiManager); + verifyNoMoreInteractions(mNMService); + + // Emulate externally-visible WifiManager effects, causing the + // per-interface state machine to start up, and telling us that + // tethering mode is to be started. + mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true); + sendWifiApStateChanged(WIFI_AP_STATE_ENABLED); + mLooper.dispatchAll(); + + assertEquals(1, mTetheringDependencies.mIsTetheringSupportedCalls); + verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER); + verify(mWifiManager).updateInterfaceIpState( + TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED); + verifyNoMoreInteractions(mNMService); + verifyNoMoreInteractions(mWifiManager); + } + + // TODO: Test with and without interfaceStatusChanged(). + @Test + public void workingWifiTetheringEnrichedApBroadcast() throws Exception { + when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true); + + // Emulate pressing the WiFi tethering button. + mTethering.startTethering(TETHERING_WIFI, null, false); + mLooper.dispatchAll(); + verify(mWifiManager, times(1)).startSoftAp(null); + verifyNoMoreInteractions(mWifiManager); + verifyNoMoreInteractions(mNMService); + + // Emulate externally-visible WifiManager effects, causing the + // per-interface state machine to start up, and telling us that + // tethering mode is to be started. + mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true); + sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED); + mLooper.dispatchAll(); + + verifyInterfaceServingModeStarted(TEST_WLAN_IFNAME); + verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER); + verify(mNMService, times(1)).setIpForwardingEnabled(true); + verify(mNMService, times(1)).startTethering(any(String[].class)); + verifyNoMoreInteractions(mNMService); + verify(mWifiManager).updateInterfaceIpState( + TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED); + verify(mWifiManager).updateInterfaceIpState( + TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED); + verifyNoMoreInteractions(mWifiManager); + verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_TETHER); + verify(mUpstreamNetworkMonitor, times(1)).startObserveAllNetworks(); + // In tethering mode, in the default configuration, an explicit request + // for a mobile network is also made. + verify(mUpstreamNetworkMonitor, times(1)).registerMobileNetworkRequest(); + // This will be called twice, one is on entering IpServer.STATE_AVAILABLE, + // and another one is on IpServer.STATE_TETHERED/IpServer.STATE_LOCAL_ONLY. + assertEquals(2, mTetheringDependencies.mIsTetheringSupportedCalls); + + ///// + // We do not currently emulate any upstream being found. + // + // This is why there are no calls to verify mNMService.enableNat() or + // mNMService.startInterfaceForwarding(). + ///// + + // Emulate pressing the WiFi tethering button. + mTethering.stopTethering(TETHERING_WIFI); + mLooper.dispatchAll(); + verify(mWifiManager, times(1)).stopSoftAp(); + verifyNoMoreInteractions(mWifiManager); + verifyNoMoreInteractions(mNMService); + + // Emulate externally-visible WifiManager effects, when tethering mode + // is being torn down. + sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED); + mTethering.interfaceRemoved(TEST_WLAN_IFNAME); + mLooper.dispatchAll(); + + verify(mNMService, times(1)).untetherInterface(TEST_WLAN_IFNAME); + // {g,s}etInterfaceConfig() called twice for enabling and disabling IPv4. + verify(mNMService, atLeastOnce()).getInterfaceConfig(TEST_WLAN_IFNAME); + verify(mNMService, atLeastOnce()) + .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class)); + verify(mNMService, times(1)).stopTethering(); + verify(mNMService, times(1)).setIpForwardingEnabled(false); + verify(mWifiManager, times(3)).updateInterfaceIpState( + TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED); + verifyNoMoreInteractions(mNMService); + verifyNoMoreInteractions(mWifiManager); + // Asking for the last error after the per-interface state machine + // has been reaped yields an unknown interface error. + assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_WLAN_IFNAME)); + } + + // TODO: Test with and without interfaceStatusChanged(). + @Test + public void failureEnablingIpForwarding() throws Exception { + when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true); + doThrow(new RemoteException()).when(mNMService).setIpForwardingEnabled(true); + + // Emulate pressing the WiFi tethering button. + mTethering.startTethering(TETHERING_WIFI, null, false); + mLooper.dispatchAll(); + verify(mWifiManager, times(1)).startSoftAp(null); + verifyNoMoreInteractions(mWifiManager); + verifyNoMoreInteractions(mNMService); + + // Emulate externally-visible WifiManager effects, causing the + // per-interface state machine to start up, and telling us that + // tethering mode is to be started. + mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true); + sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED); + mLooper.dispatchAll(); + + // We verify get/set called thrice here: twice for setup (on NMService) and once during + // teardown (on Netd) because all events happen over the course of the single + // dispatchAll() above. Note that once the IpServer IPv4 address config + // code is refactored the two calls during shutdown will revert to one. + verify(mNMService, times(2)).getInterfaceConfig(TEST_WLAN_IFNAME); + verify(mNMService, times(2)) + .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class)); + verify(mNetd, times(1)).interfaceSetCfg(argThat(p -> TEST_WLAN_IFNAME.equals(p.ifName))); + verify(mNMService, times(1)).tetherInterface(TEST_WLAN_IFNAME); + verify(mWifiManager).updateInterfaceIpState( + TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED); + verify(mWifiManager).updateInterfaceIpState( + TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED); + // There are 3 state change event: + // AVAILABLE -> STATE_TETHERED -> STATE_AVAILABLE. + assertEquals(3, mTetheringDependencies.mIsTetheringSupportedCalls); + verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER); + // This is called, but will throw. + verify(mNMService, times(1)).setIpForwardingEnabled(true); + // This never gets called because of the exception thrown above. + verify(mNMService, times(0)).startTethering(any(String[].class)); + // When the master state machine transitions to an error state it tells + // downstream interfaces, which causes us to tell Wi-Fi about the error + // so it can take down AP mode. + verify(mNMService, times(1)).untetherInterface(TEST_WLAN_IFNAME); + verify(mWifiManager).updateInterfaceIpState( + TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR); + + verifyNoMoreInteractions(mWifiManager); + verifyNoMoreInteractions(mNMService); + } + + private void userRestrictionsListenerBehaviour( + boolean currentDisallow, boolean nextDisallow, String[] activeTetheringIfacesList, + int expectedInteractionsWithShowNotification) throws Exception { + final int userId = 0; + final Bundle currRestrictions = new Bundle(); + final Bundle newRestrictions = new Bundle(); + Tethering tethering = mock(Tethering.class); + Tethering.TetheringUserRestrictionListener turl = + new Tethering.TetheringUserRestrictionListener(tethering); + + currRestrictions.putBoolean(UserManager.DISALLOW_CONFIG_TETHERING, currentDisallow); + newRestrictions.putBoolean(UserManager.DISALLOW_CONFIG_TETHERING, nextDisallow); + when(tethering.getTetheredIfaces()).thenReturn(activeTetheringIfacesList); + + turl.onUserRestrictionsChanged(userId, newRestrictions, currRestrictions); + + verify(tethering, times(expectedInteractionsWithShowNotification)) + .showTetheredNotification(anyInt(), eq(false)); + + verify(tethering, times(expectedInteractionsWithShowNotification)).untetherAll(); + } + + @Test + public void testDisallowTetheringWhenNoTetheringInterfaceIsActive() throws Exception { + final String[] emptyActiveIfacesList = new String[]{}; + final boolean currDisallow = false; + final boolean nextDisallow = true; + final int expectedInteractionsWithShowNotification = 0; + + userRestrictionsListenerBehaviour(currDisallow, nextDisallow, emptyActiveIfacesList, + expectedInteractionsWithShowNotification); + } + + @Test + public void testDisallowTetheringWhenAtLeastOneTetheringInterfaceIsActive() throws Exception { + final String[] nonEmptyActiveIfacesList = new String[]{TEST_WLAN_IFNAME}; + final boolean currDisallow = false; + final boolean nextDisallow = true; + final int expectedInteractionsWithShowNotification = 1; + + userRestrictionsListenerBehaviour(currDisallow, nextDisallow, nonEmptyActiveIfacesList, + expectedInteractionsWithShowNotification); + } + + @Test + public void testAllowTetheringWhenNoTetheringInterfaceIsActive() throws Exception { + final String[] nonEmptyActiveIfacesList = new String[]{}; + final boolean currDisallow = true; + final boolean nextDisallow = false; + final int expectedInteractionsWithShowNotification = 0; + + userRestrictionsListenerBehaviour(currDisallow, nextDisallow, nonEmptyActiveIfacesList, + expectedInteractionsWithShowNotification); + } + + @Test + public void testAllowTetheringWhenAtLeastOneTetheringInterfaceIsActive() throws Exception { + final String[] nonEmptyActiveIfacesList = new String[]{TEST_WLAN_IFNAME}; + final boolean currDisallow = true; + final boolean nextDisallow = false; + final int expectedInteractionsWithShowNotification = 0; + + userRestrictionsListenerBehaviour(currDisallow, nextDisallow, nonEmptyActiveIfacesList, + expectedInteractionsWithShowNotification); + } + + @Test + public void testDisallowTetheringUnchanged() throws Exception { + final String[] nonEmptyActiveIfacesList = new String[]{TEST_WLAN_IFNAME}; + final int expectedInteractionsWithShowNotification = 0; + boolean currDisallow = true; + boolean nextDisallow = true; + + userRestrictionsListenerBehaviour(currDisallow, nextDisallow, nonEmptyActiveIfacesList, + expectedInteractionsWithShowNotification); + + currDisallow = false; + nextDisallow = false; + + userRestrictionsListenerBehaviour(currDisallow, nextDisallow, nonEmptyActiveIfacesList, + expectedInteractionsWithShowNotification); + } + + private class TestTetherInternalCallback extends ITetherInternalCallback.Stub { + private final ArrayList mActualUpstreams = new ArrayList<>(); + private final ArrayList mTetheringConfigs = + new ArrayList<>(); + private final ArrayList mTetherStates = new ArrayList<>(); + + // This function will remove the recorded callbacks, so it must be called once for + // each callback. If this is called after multiple callback, the order matters. + // onCallbackCreated counts as the first call to expectUpstreamChanged with + // @see onCallbackCreated. + public void expectUpstreamChanged(Network... networks) { + if (networks == null) { + assertNoUpstreamChangeCallback(); + return; + } + + final ArrayList expectedUpstreams = + new ArrayList(Arrays.asList(networks)); + for (Network upstream : expectedUpstreams) { + // throws OOB if no expectations + assertEquals(mActualUpstreams.remove(0), upstream); + } + assertNoUpstreamChangeCallback(); + } + + // This function will remove the recorded callbacks, so it must be called once + // for each callback. If this is called after multiple callback, the order matters. + // onCallbackCreated counts as the first call to onConfigurationChanged with + // @see onCallbackCreated. + public void expectConfigurationChanged(TetheringConfigurationParcel... tetherConfigs) { + final ArrayList expectedTetherConfig = + new ArrayList(Arrays.asList(tetherConfigs)); + for (TetheringConfigurationParcel config : expectedTetherConfig) { + // throws OOB if no expectations + final TetheringConfigurationParcel actualConfig = mTetheringConfigs.remove(0); + assertTetherConfigParcelEqual(actualConfig, config); + } + assertNoConfigChangeCallback(); + } + + public TetherStatesParcel pollTetherStatesChanged() { + assertStateChangeCallback(); + return mTetherStates.remove(0); + } + + @Override + public void onUpstreamChanged(Network network) { + mActualUpstreams.add(network); + } + + @Override + public void onConfigurationChanged(TetheringConfigurationParcel config) { + mTetheringConfigs.add(config); + } + + @Override + public void onTetherStatesChanged(TetherStatesParcel states) { + mTetherStates.add(states); + } + + @Override + public void onCallbackCreated(Network network, TetheringConfigurationParcel config, + TetherStatesParcel states) { + mActualUpstreams.add(network); + mTetheringConfigs.add(config); + mTetherStates.add(states); + } + + public void assertNoUpstreamChangeCallback() { + assertTrue(mActualUpstreams.isEmpty()); + } + + public void assertNoConfigChangeCallback() { + assertTrue(mTetheringConfigs.isEmpty()); + } + + public void assertStateChangeCallback() { + assertFalse(mTetherStates.isEmpty()); + } + + private void assertTetherConfigParcelEqual(@NonNull TetheringConfigurationParcel actual, + @NonNull TetheringConfigurationParcel expect) { + assertEquals(actual.subId, expect.subId); + assertArrayEquals(actual.tetherableUsbRegexs, expect.tetherableUsbRegexs); + assertArrayEquals(actual.tetherableWifiRegexs, expect.tetherableWifiRegexs); + assertArrayEquals(actual.tetherableBluetoothRegexs, expect.tetherableBluetoothRegexs); + assertEquals(actual.isDunRequired, expect.isDunRequired); + assertEquals(actual.chooseUpstreamAutomatically, expect.chooseUpstreamAutomatically); + assertArrayEquals(actual.preferredUpstreamIfaceTypes, + expect.preferredUpstreamIfaceTypes); + assertArrayEquals(actual.legacyDhcpRanges, expect.legacyDhcpRanges); + assertArrayEquals(actual.defaultIPv4DNS, expect.defaultIPv4DNS); + assertEquals(actual.enableLegacyDhcpServer, expect.enableLegacyDhcpServer); + assertArrayEquals(actual.provisioningApp, expect.provisioningApp); + assertEquals(actual.provisioningAppNoUi, expect.provisioningAppNoUi); + assertEquals(actual.provisioningCheckPeriod, expect.provisioningCheckPeriod); + } + } + + @Test + public void testRegisterTetherInternalCallback() throws Exception { + TestTetherInternalCallback callback = new TestTetherInternalCallback(); + + // 1. Register one callback before running any tethering. + mTethering.registerTetherInternalCallback(callback); + mLooper.dispatchAll(); + callback.expectUpstreamChanged(new Network[] {null}); + callback.expectConfigurationChanged( + mTethering.getTetheringConfiguration().toStableParcelable()); + TetherStatesParcel tetherState = callback.pollTetherStatesChanged(); + assertEquals(tetherState, null); + // 2. Enable wifi tethering + NetworkState upstreamState = buildMobileDualStackUpstreamState(); + when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState); + when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())) + .thenReturn(upstreamState); + when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true); + mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true); + mLooper.dispatchAll(); + tetherState = callback.pollTetherStatesChanged(); + assertArrayEquals(tetherState.availableList, new String[] {TEST_WLAN_IFNAME}); + + mTethering.startTethering(TETHERING_WIFI, null, false); + sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED); + mLooper.dispatchAll(); + tetherState = callback.pollTetherStatesChanged(); + assertArrayEquals(tetherState.tetheredList, new String[] {TEST_WLAN_IFNAME}); + callback.expectUpstreamChanged(upstreamState.network); + + // 3. Disable wifi tethering. + mTethering.stopTethering(TETHERING_WIFI); + sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED); + mLooper.dispatchAll(); + tetherState = callback.pollTetherStatesChanged(); + assertArrayEquals(tetherState.availableList, new String[] {TEST_WLAN_IFNAME}); + mLooper.dispatchAll(); + callback.expectUpstreamChanged(new Network[] {null}); + } + + @Test + public void testMultiSimAware() throws Exception { + final TetheringConfiguration initailConfig = mTethering.getTetheringConfiguration(); + assertEquals(INVALID_SUBSCRIPTION_ID, initailConfig.subId); + + final int fakeSubId = 1234; + mPhoneStateListener.onActiveDataSubscriptionIdChanged(fakeSubId); + final TetheringConfiguration newConfig = mTethering.getTetheringConfiguration(); + assertEquals(fakeSubId, newConfig.subId); + } + + private void workingWifiP2pGroupOwner( + boolean emulateInterfaceStatusChanged) throws Exception { + if (emulateInterfaceStatusChanged) { + mTethering.interfaceStatusChanged(TEST_P2P_IFNAME, true); + } + sendWifiP2pConnectionChanged(true, true, TEST_P2P_IFNAME); + mLooper.dispatchAll(); + + verifyInterfaceServingModeStarted(TEST_P2P_IFNAME); + verifyTetheringBroadcast(TEST_P2P_IFNAME, EXTRA_AVAILABLE_TETHER); + verify(mNMService, times(1)).setIpForwardingEnabled(true); + verify(mNMService, times(1)).startTethering(any(String[].class)); + verifyNoMoreInteractions(mNMService); + verifyTetheringBroadcast(TEST_P2P_IFNAME, EXTRA_ACTIVE_LOCAL_ONLY); + verify(mUpstreamNetworkMonitor, times(1)).startObserveAllNetworks(); + // This will be called twice, one is on entering IpServer.STATE_AVAILABLE, + // and another one is on IpServer.STATE_TETHERED/IpServer.STATE_LOCAL_ONLY. + assertEquals(2, mTetheringDependencies.mIsTetheringSupportedCalls); + + assertEquals(TETHER_ERROR_NO_ERROR, mTethering.getLastTetherError(TEST_P2P_IFNAME)); + + // Emulate externally-visible WifiP2pManager effects, when wifi p2p group + // is being removed. + sendWifiP2pConnectionChanged(false, true, TEST_P2P_IFNAME); + mTethering.interfaceRemoved(TEST_P2P_IFNAME); + mLooper.dispatchAll(); + + verify(mNMService, times(1)).untetherInterface(TEST_P2P_IFNAME); + // {g,s}etInterfaceConfig() called twice for enabling and disabling IPv4. + verify(mNMService, times(2)).getInterfaceConfig(TEST_P2P_IFNAME); + verify(mNMService, times(2)) + .setInterfaceConfig(eq(TEST_P2P_IFNAME), any(InterfaceConfiguration.class)); + verify(mNMService, times(1)).stopTethering(); + verify(mNMService, times(1)).setIpForwardingEnabled(false); + verify(mUpstreamNetworkMonitor, never()).getCurrentPreferredUpstream(); + verify(mUpstreamNetworkMonitor, never()).selectPreferredUpstreamType(any()); + verifyNoMoreInteractions(mNMService); + // Asking for the last error after the per-interface state machine + // has been reaped yields an unknown interface error. + assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_P2P_IFNAME)); + } + + private void workingWifiP2pGroupClient( + boolean emulateInterfaceStatusChanged) throws Exception { + if (emulateInterfaceStatusChanged) { + mTethering.interfaceStatusChanged(TEST_P2P_IFNAME, true); + } + sendWifiP2pConnectionChanged(true, false, TEST_P2P_IFNAME); + mLooper.dispatchAll(); + + verify(mNMService, never()).getInterfaceConfig(TEST_P2P_IFNAME); + verify(mNMService, never()) + .setInterfaceConfig(eq(TEST_P2P_IFNAME), any(InterfaceConfiguration.class)); + verify(mNMService, never()).tetherInterface(TEST_P2P_IFNAME); + verify(mNMService, never()).setIpForwardingEnabled(true); + verify(mNMService, never()).startTethering(any(String[].class)); + + // Emulate externally-visible WifiP2pManager effects, when wifi p2p group + // is being removed. + sendWifiP2pConnectionChanged(false, false, TEST_P2P_IFNAME); + mTethering.interfaceRemoved(TEST_P2P_IFNAME); + mLooper.dispatchAll(); + + verify(mNMService, never()).untetherInterface(TEST_P2P_IFNAME); + verify(mNMService, never()).getInterfaceConfig(TEST_P2P_IFNAME); + verify(mNMService, never()) + .setInterfaceConfig(eq(TEST_P2P_IFNAME), any(InterfaceConfiguration.class)); + verify(mNMService, never()).stopTethering(); + verify(mNMService, never()).setIpForwardingEnabled(false); + verifyNoMoreInteractions(mNMService); + // Asking for the last error after the per-interface state machine + // has been reaped yields an unknown interface error. + assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_P2P_IFNAME)); + } + + @Test + public void workingWifiP2pGroupOwnerWithIfaceChanged() throws Exception { + workingWifiP2pGroupOwner(true); + } + + @Test + public void workingWifiP2pGroupOwnerSansIfaceChanged() throws Exception { + workingWifiP2pGroupOwner(false); + } + + private void workingWifiP2pGroupOwnerLegacyMode( + boolean emulateInterfaceStatusChanged) throws Exception { + // change to legacy mode and update tethering information by chaning SIM + when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_p2p_regexs)) + .thenReturn(new String[]{}); + final int fakeSubId = 1234; + mPhoneStateListener.onActiveDataSubscriptionIdChanged(fakeSubId); + + if (emulateInterfaceStatusChanged) { + mTethering.interfaceStatusChanged(TEST_P2P_IFNAME, true); + } + sendWifiP2pConnectionChanged(true, true, TEST_P2P_IFNAME); + mLooper.dispatchAll(); + + verify(mNMService, never()).getInterfaceConfig(TEST_P2P_IFNAME); + verify(mNMService, never()) + .setInterfaceConfig(eq(TEST_P2P_IFNAME), any(InterfaceConfiguration.class)); + verify(mNMService, never()).tetherInterface(TEST_P2P_IFNAME); + verify(mNMService, never()).setIpForwardingEnabled(true); + verify(mNMService, never()).startTethering(any(String[].class)); + assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_P2P_IFNAME)); + } + @Test + public void workingWifiP2pGroupOwnerLegacyModeWithIfaceChanged() throws Exception { + workingWifiP2pGroupOwnerLegacyMode(true); + } + + @Test + public void workingWifiP2pGroupOwnerLegacyModeSansIfaceChanged() throws Exception { + workingWifiP2pGroupOwnerLegacyMode(false); + } + + @Test + public void workingWifiP2pGroupClientWithIfaceChanged() throws Exception { + workingWifiP2pGroupClient(true); + } + + @Test + public void workingWifiP2pGroupClientSansIfaceChanged() throws Exception { + workingWifiP2pGroupClient(false); + } + + // TODO: Test that a request for hotspot mode doesn't interfere with an + // already operating tethering mode interface. +} From 499c4a8c757e9a6d30f23b7a86a5e7311b84fd65 Mon Sep 17 00:00:00 2001 From: markchien Date: Wed, 27 Nov 2019 21:20:33 +0800 Subject: [PATCH 005/188] [Tether10]Remove UserManagerInternal usage in Tethering To be the unbundled application, tethering can not use UserManagerInternal method anymore. Replace it by listening user restriction change intent. Also fix some cleanup from https://android-review.googlesource.com/c/platform/frameworks/base/+/1131002 Bug: 143195885 Test: -build, flash, boot -atest TetheringTests -manual test. Add a trigger point to call UserManager.setUserRestriction(DISALLOW_CONFIG_TETHERING) from Settings. Open hotspot -> restrict tethering from new adding trigger point -> hotspot is OFF and Tethering settings are not available. Change-Id: I4ed08d20f8b7476b8ecd5d018b75806dd2f22cc1 --- .../net/TetheringConfigurationParcel.aidl | 2 +- .../src/android/net/TetheringManager.java | 10 +++- .../connectivity/tethering/Tethering.java | 49 ++++++++++++------- .../connectivity/tethering/TetheringTest.java | 38 +++++++------- 4 files changed, 59 insertions(+), 40 deletions(-) diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringConfigurationParcel.aidl b/Tethering/common/TetheringLib/src/android/net/TetheringConfigurationParcel.aidl index fdf0d16057..89f38132ff 100644 --- a/Tethering/common/TetheringLib/src/android/net/TetheringConfigurationParcel.aidl +++ b/Tethering/common/TetheringLib/src/android/net/TetheringConfigurationParcel.aidl @@ -17,7 +17,7 @@ package android.net; /** - * Configuration details for a tethering. + * Configuration details for tethering. * @hide */ parcelable TetheringConfigurationParcel { diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java index 9e8726edf0..eb0d443f4b 100644 --- a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java +++ b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java @@ -168,11 +168,12 @@ public class TetheringManager { * #getTetherableIfaces() to ensure corresponding interface is available for * tethering before calling #tether(). * - * TODO: Deprecate this API. The only usages should be in PanService and Wifi P2P which + * @deprecated The only usages should be in PanService and Wifi P2P which * need direct access. * * {@hide} */ + @Deprecated public int tether(@NonNull String iface) { try { mConnector.tether(iface); @@ -185,8 +186,10 @@ public class TetheringManager { /** * Stop tethering the named interface. * + * @deprecated * {@hide} */ + @Deprecated public int untether(@NonNull String iface) { try { mConnector.untether(iface); @@ -202,9 +205,10 @@ public class TetheringManager { * 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. * - * @Deprecated + * @deprecated * {@hide} */ + @Deprecated public int setUsbTethering(boolean enable) { try { mConnector.setUsbTethering(enable); @@ -386,8 +390,10 @@ public class TetheringManager { /** * Get the set of tethered dhcp ranges. * + * @deprecated This API just return the default value which is not used in DhcpServer. * {@hide} */ + @Deprecated public @NonNull String[] getTetheredDhcpRanges() { if (!mCallback.awaitCallbackCreation()) { throw new NullPointerException("callback was not ready yet"); diff --git a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java index 058fb4fe98..f1228129cd 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java +++ b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java @@ -93,8 +93,6 @@ import android.os.RemoteException; import android.os.ResultReceiver; import android.os.UserHandle; import android.os.UserManager; -import android.os.UserManagerInternal; -import android.os.UserManagerInternal.UserRestrictionsListener; import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; import android.text.TextUtils; @@ -111,7 +109,6 @@ import com.android.internal.util.MessageUtils; import com.android.internal.util.Protocol; import com.android.internal.util.State; import com.android.internal.util.StateMachine; -import com.android.server.LocalServices; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -188,6 +185,7 @@ public class Tethering { private final PhoneStateListener mPhoneStateListener; private final INetd mNetd; private final NetdCallback mNetdCallback; + private final UserRestrictionActionListener mTetheringRestriction; private int mActiveDataSubId = INVALID_SUBSCRIPTION_ID; // All the usage of mTetherInternalCallback should run in the same thread. private ITetherInternalCallback mTetherInternalCallback = null; @@ -280,6 +278,10 @@ public class Tethering { mLog.e("Unable to register netd UnsolicitedEventListener"); } + final UserManager userManager = (UserManager) mContext.getSystemService( + Context.USER_SERVICE); + mTetheringRestriction = new UserRestrictionActionListener(userManager, this); + // Load tethering configuration. updateConfiguration(); @@ -298,6 +300,7 @@ public class Tethering { filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); filter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION); + filter.addAction(UserManager.ACTION_USER_RESTRICTIONS_CHANGED); mContext.registerReceiver(mStateReceiver, filter, null, handler); filter = new IntentFilter(); @@ -306,11 +309,6 @@ public class Tethering { filter.addDataScheme("file"); mContext.registerReceiver(mStateReceiver, filter, null, handler); - final UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class); - // This check is useful only for some unit tests; example: ConnectivityServiceTest. - if (umi != null) { - umi.addUserRestrictionsListener(new TetheringUserRestrictionListener(this)); - } } private WifiManager getWifiManager() { @@ -717,8 +715,8 @@ public class Tethering { } if (mTetheredNotificationBuilder == null) { - mTetheredNotificationBuilder = - new Notification.Builder(mContext, SystemNotificationChannels.NETWORK_STATUS); + mTetheredNotificationBuilder = new Notification.Builder(mContext, + SystemNotificationChannels.NETWORK_STATUS); mTetheredNotificationBuilder.setWhen(0) .setOngoing(true) .setColor(mContext.getColor( @@ -764,6 +762,9 @@ public class Tethering { } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) { mLog.log("OBSERVED configuration changed"); updateConfiguration(); + } else if (action.equals(UserManager.ACTION_USER_RESTRICTIONS_CHANGED)) { + mLog.log("OBSERVED user restrictions changed"); + handleUserRestrictionAction(); } } @@ -870,25 +871,35 @@ public class Tethering { } } } + + private void handleUserRestrictionAction() { + mTetheringRestriction.onUserRestrictionsChanged(); + } } @VisibleForTesting - protected static class TetheringUserRestrictionListener implements UserRestrictionsListener { + protected static class UserRestrictionActionListener { + private final UserManager mUserManager; private final Tethering mWrapper; + public boolean mDisallowTethering; - public TetheringUserRestrictionListener(Tethering wrapper) { + public UserRestrictionActionListener(UserManager um, Tethering wrapper) { + mUserManager = um; mWrapper = wrapper; + mDisallowTethering = false; } - public void onUserRestrictionsChanged(int userId, - Bundle newRestrictions, - Bundle prevRestrictions) { + public void onUserRestrictionsChanged() { + // getUserRestrictions gets restriction for this process' user, which is the primary + // user. This is fine because DISALLOW_CONFIG_TETHERING can only be set on the primary + // user. See UserManager.DISALLOW_CONFIG_TETHERING. + final Bundle restrictions = mUserManager.getUserRestrictions(); final boolean newlyDisallowed = - newRestrictions.getBoolean(UserManager.DISALLOW_CONFIG_TETHERING); - final boolean previouslyDisallowed = - prevRestrictions.getBoolean(UserManager.DISALLOW_CONFIG_TETHERING); - final boolean tetheringDisallowedChanged = (newlyDisallowed != previouslyDisallowed); + restrictions.getBoolean(UserManager.DISALLOW_CONFIG_TETHERING); + final boolean prevDisallowed = mDisallowTethering; + mDisallowTethering = newlyDisallowed; + final boolean tetheringDisallowedChanged = (newlyDisallowed != prevDisallowed); if (!tetheringDisallowedChanged) { return; } diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java index 4f6f61bf95..0273ed3586 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java @@ -166,6 +166,7 @@ public class TetheringTest { @Mock private RouterAdvertisementDaemon mRouterAdvertisementDaemon; @Mock private IDhcpServer mDhcpServer; @Mock private INetd mNetd; + @Mock private UserManager mUserManager; private final MockIpServerDependencies mIpServerDependencies = spy(new MockIpServerDependencies()); @@ -214,6 +215,7 @@ public class TetheringTest { if (Context.WIFI_SERVICE.equals(name)) return mWifiManager; if (Context.USB_SERVICE.equals(name)) return mUsbManager; if (Context.TELEPHONY_SERVICE.equals(name)) return mTelephonyManager; + if (Context.USER_SERVICE.equals(name)) return mUserManager; return super.getSystemService(name); } @@ -954,26 +956,26 @@ public class TetheringTest { verifyNoMoreInteractions(mNMService); } - private void userRestrictionsListenerBehaviour( + private void runUserRestrictionsChange( boolean currentDisallow, boolean nextDisallow, String[] activeTetheringIfacesList, int expectedInteractionsWithShowNotification) throws Exception { - final int userId = 0; - final Bundle currRestrictions = new Bundle(); final Bundle newRestrictions = new Bundle(); - Tethering tethering = mock(Tethering.class); - Tethering.TetheringUserRestrictionListener turl = - new Tethering.TetheringUserRestrictionListener(tethering); - - currRestrictions.putBoolean(UserManager.DISALLOW_CONFIG_TETHERING, currentDisallow); newRestrictions.putBoolean(UserManager.DISALLOW_CONFIG_TETHERING, nextDisallow); - when(tethering.getTetheredIfaces()).thenReturn(activeTetheringIfacesList); + final Tethering mockTethering = mock(Tethering.class); + when(mockTethering.getTetheredIfaces()).thenReturn(activeTetheringIfacesList); + when(mUserManager.getUserRestrictions()).thenReturn(newRestrictions); - turl.onUserRestrictionsChanged(userId, newRestrictions, currRestrictions); + final Tethering.UserRestrictionActionListener ural = + new Tethering.UserRestrictionActionListener(mUserManager, mockTethering); + ural.mDisallowTethering = currentDisallow; - verify(tethering, times(expectedInteractionsWithShowNotification)) + ural.onUserRestrictionsChanged(); + + verify(mockTethering, times(expectedInteractionsWithShowNotification)) .showTetheredNotification(anyInt(), eq(false)); - verify(tethering, times(expectedInteractionsWithShowNotification)).untetherAll(); + verify(mockTethering, times(expectedInteractionsWithShowNotification)) + .untetherAll(); } @Test @@ -983,7 +985,7 @@ public class TetheringTest { final boolean nextDisallow = true; final int expectedInteractionsWithShowNotification = 0; - userRestrictionsListenerBehaviour(currDisallow, nextDisallow, emptyActiveIfacesList, + runUserRestrictionsChange(currDisallow, nextDisallow, emptyActiveIfacesList, expectedInteractionsWithShowNotification); } @@ -994,7 +996,7 @@ public class TetheringTest { final boolean nextDisallow = true; final int expectedInteractionsWithShowNotification = 1; - userRestrictionsListenerBehaviour(currDisallow, nextDisallow, nonEmptyActiveIfacesList, + runUserRestrictionsChange(currDisallow, nextDisallow, nonEmptyActiveIfacesList, expectedInteractionsWithShowNotification); } @@ -1005,7 +1007,7 @@ public class TetheringTest { final boolean nextDisallow = false; final int expectedInteractionsWithShowNotification = 0; - userRestrictionsListenerBehaviour(currDisallow, nextDisallow, nonEmptyActiveIfacesList, + runUserRestrictionsChange(currDisallow, nextDisallow, nonEmptyActiveIfacesList, expectedInteractionsWithShowNotification); } @@ -1016,7 +1018,7 @@ public class TetheringTest { final boolean nextDisallow = false; final int expectedInteractionsWithShowNotification = 0; - userRestrictionsListenerBehaviour(currDisallow, nextDisallow, nonEmptyActiveIfacesList, + runUserRestrictionsChange(currDisallow, nextDisallow, nonEmptyActiveIfacesList, expectedInteractionsWithShowNotification); } @@ -1027,13 +1029,13 @@ public class TetheringTest { boolean currDisallow = true; boolean nextDisallow = true; - userRestrictionsListenerBehaviour(currDisallow, nextDisallow, nonEmptyActiveIfacesList, + runUserRestrictionsChange(currDisallow, nextDisallow, nonEmptyActiveIfacesList, expectedInteractionsWithShowNotification); currDisallow = false; nextDisallow = false; - userRestrictionsListenerBehaviour(currDisallow, nextDisallow, nonEmptyActiveIfacesList, + runUserRestrictionsChange(currDisallow, nextDisallow, nonEmptyActiveIfacesList, expectedInteractionsWithShowNotification); } From 0988213c6066453dcd119b8906640f02cb085c33 Mon Sep 17 00:00:00 2001 From: paulhu Date: Mon, 2 Dec 2019 19:54:03 +0800 Subject: [PATCH 006/188] Migrate tethering notification resources Migrate the resources which are only used by tethering notification from framework/base/core/res to tethering package resource folder. Bug: 122085773 Bug: 139763854 Test: build pass Change-Id: I832103090d500fd8e2e2efb9dc7151426da3f88d --- .../stat_sys_tether_bluetooth.png | Bin 0 -> 891 bytes .../drawable-hdpi/stat_sys_tether_general.png | Bin 0 -> 963 bytes .../res/drawable-hdpi/stat_sys_tether_usb.png | Bin 0 -> 909 bytes .../stat_sys_tether_bluetooth.png | Bin 0 -> 534 bytes .../drawable-ldpi/stat_sys_tether_general.png | Bin 0 -> 554 bytes .../res/drawable-ldpi/stat_sys_tether_usb.png | Bin 0 -> 518 bytes .../stat_sys_tether_bluetooth.png | Bin 0 -> 675 bytes .../drawable-mdpi/stat_sys_tether_general.png | Bin 0 -> 659 bytes .../res/drawable-mdpi/stat_sys_tether_usb.png | Bin 0 -> 698 bytes .../stat_sys_tether_bluetooth.png | Bin 0 -> 1111 bytes .../stat_sys_tether_general.png | Bin 0 -> 1274 bytes .../drawable-xhdpi/stat_sys_tether_usb.png | Bin 0 -> 1182 bytes .../stat_sys_tether_bluetooth.png | Bin 0 -> 1046 bytes .../stat_sys_tether_general.png | Bin 0 -> 1219 bytes .../drawable-xxhdpi/stat_sys_tether_usb.png | Bin 0 -> 1002 bytes Tethering/res/values-af/strings.xml | 8 ++++++++ Tethering/res/values-am/strings.xml | 8 ++++++++ Tethering/res/values-ar/strings.xml | 8 ++++++++ Tethering/res/values-as/strings.xml | 8 ++++++++ Tethering/res/values-az/strings.xml | 8 ++++++++ Tethering/res/values-b+sr+Latn/strings.xml | 8 ++++++++ Tethering/res/values-be/strings.xml | 8 ++++++++ Tethering/res/values-bg/strings.xml | 8 ++++++++ Tethering/res/values-bn/strings.xml | 8 ++++++++ Tethering/res/values-bs/strings.xml | 8 ++++++++ Tethering/res/values-ca/strings.xml | 8 ++++++++ Tethering/res/values-cs/strings.xml | 8 ++++++++ Tethering/res/values-da/strings.xml | 8 ++++++++ Tethering/res/values-de/strings.xml | 8 ++++++++ Tethering/res/values-el/strings.xml | 8 ++++++++ Tethering/res/values-en-rAU/strings.xml | 8 ++++++++ Tethering/res/values-en-rCA/strings.xml | 8 ++++++++ Tethering/res/values-en-rGB/strings.xml | 8 ++++++++ Tethering/res/values-en-rIN/strings.xml | 8 ++++++++ Tethering/res/values-en-rXC/strings.xml | 8 ++++++++ Tethering/res/values-es-rUS/strings.xml | 8 ++++++++ Tethering/res/values-es/strings.xml | 8 ++++++++ Tethering/res/values-et/strings.xml | 8 ++++++++ Tethering/res/values-eu/strings.xml | 8 ++++++++ Tethering/res/values-fa/strings.xml | 8 ++++++++ Tethering/res/values-fi/strings.xml | 8 ++++++++ Tethering/res/values-fr-rCA/strings.xml | 8 ++++++++ Tethering/res/values-fr/strings.xml | 8 ++++++++ Tethering/res/values-gl/strings.xml | 8 ++++++++ Tethering/res/values-gu/strings.xml | 8 ++++++++ Tethering/res/values-hi/strings.xml | 8 ++++++++ Tethering/res/values-hr/strings.xml | 8 ++++++++ Tethering/res/values-hu/strings.xml | 8 ++++++++ Tethering/res/values-hy/strings.xml | 8 ++++++++ Tethering/res/values-in/strings.xml | 8 ++++++++ Tethering/res/values-is/strings.xml | 8 ++++++++ Tethering/res/values-it/strings.xml | 8 ++++++++ Tethering/res/values-iw/strings.xml | 8 ++++++++ Tethering/res/values-ja/strings.xml | 8 ++++++++ Tethering/res/values-ka/strings.xml | 8 ++++++++ Tethering/res/values-kk/strings.xml | 8 ++++++++ Tethering/res/values-km/strings.xml | 8 ++++++++ Tethering/res/values-kn/strings.xml | 8 ++++++++ Tethering/res/values-ko/strings.xml | 8 ++++++++ Tethering/res/values-ky/strings.xml | 8 ++++++++ Tethering/res/values-lo/strings.xml | 8 ++++++++ Tethering/res/values-lt/strings.xml | 8 ++++++++ Tethering/res/values-lv/strings.xml | 8 ++++++++ Tethering/res/values-mk/strings.xml | 8 ++++++++ Tethering/res/values-ml/strings.xml | 8 ++++++++ Tethering/res/values-mn/strings.xml | 8 ++++++++ Tethering/res/values-mr/strings.xml | 8 ++++++++ Tethering/res/values-ms/strings.xml | 8 ++++++++ Tethering/res/values-my/strings.xml | 8 ++++++++ Tethering/res/values-nb/strings.xml | 8 ++++++++ Tethering/res/values-ne/strings.xml | 8 ++++++++ Tethering/res/values-nl/strings.xml | 8 ++++++++ Tethering/res/values-or/strings.xml | 8 ++++++++ Tethering/res/values-pa/strings.xml | 8 ++++++++ Tethering/res/values-pl/strings.xml | 8 ++++++++ Tethering/res/values-pt-rBR/strings.xml | 8 ++++++++ Tethering/res/values-pt-rPT/strings.xml | 8 ++++++++ Tethering/res/values-pt/strings.xml | 8 ++++++++ Tethering/res/values-ro/strings.xml | 8 ++++++++ Tethering/res/values-ru/strings.xml | 8 ++++++++ Tethering/res/values-si/strings.xml | 8 ++++++++ Tethering/res/values-sk/strings.xml | 8 ++++++++ Tethering/res/values-sl/strings.xml | 8 ++++++++ Tethering/res/values-sq/strings.xml | 8 ++++++++ Tethering/res/values-sr/strings.xml | 8 ++++++++ Tethering/res/values-sv/strings.xml | 8 ++++++++ Tethering/res/values-sw/strings.xml | 8 ++++++++ Tethering/res/values-ta/strings.xml | 8 ++++++++ Tethering/res/values-te/strings.xml | 8 ++++++++ Tethering/res/values-th/strings.xml | 8 ++++++++ Tethering/res/values-tl/strings.xml | 8 ++++++++ Tethering/res/values-tr/strings.xml | 8 ++++++++ Tethering/res/values-uk/strings.xml | 8 ++++++++ Tethering/res/values-ur/strings.xml | 8 ++++++++ Tethering/res/values-uz/strings.xml | 8 ++++++++ Tethering/res/values-vi/strings.xml | 8 ++++++++ Tethering/res/values-zh-rCN/strings.xml | 8 ++++++++ Tethering/res/values-zh-rHK/strings.xml | 8 ++++++++ Tethering/res/values-zh-rTW/strings.xml | 8 ++++++++ Tethering/res/values-zu/strings.xml | 8 ++++++++ Tethering/res/values/strings.xml | 16 +++++++++++++++ .../connectivity/tethering/Tethering.java | 19 +++++++++--------- 102 files changed, 706 insertions(+), 9 deletions(-) create mode 100644 Tethering/res/drawable-hdpi/stat_sys_tether_bluetooth.png create mode 100644 Tethering/res/drawable-hdpi/stat_sys_tether_general.png create mode 100644 Tethering/res/drawable-hdpi/stat_sys_tether_usb.png create mode 100644 Tethering/res/drawable-ldpi/stat_sys_tether_bluetooth.png create mode 100644 Tethering/res/drawable-ldpi/stat_sys_tether_general.png create mode 100644 Tethering/res/drawable-ldpi/stat_sys_tether_usb.png create mode 100644 Tethering/res/drawable-mdpi/stat_sys_tether_bluetooth.png create mode 100644 Tethering/res/drawable-mdpi/stat_sys_tether_general.png create mode 100644 Tethering/res/drawable-mdpi/stat_sys_tether_usb.png create mode 100644 Tethering/res/drawable-xhdpi/stat_sys_tether_bluetooth.png create mode 100644 Tethering/res/drawable-xhdpi/stat_sys_tether_general.png create mode 100644 Tethering/res/drawable-xhdpi/stat_sys_tether_usb.png create mode 100644 Tethering/res/drawable-xxhdpi/stat_sys_tether_bluetooth.png create mode 100644 Tethering/res/drawable-xxhdpi/stat_sys_tether_general.png create mode 100644 Tethering/res/drawable-xxhdpi/stat_sys_tether_usb.png create mode 100644 Tethering/res/values-af/strings.xml create mode 100644 Tethering/res/values-am/strings.xml create mode 100644 Tethering/res/values-ar/strings.xml create mode 100644 Tethering/res/values-as/strings.xml create mode 100644 Tethering/res/values-az/strings.xml create mode 100644 Tethering/res/values-b+sr+Latn/strings.xml create mode 100644 Tethering/res/values-be/strings.xml create mode 100644 Tethering/res/values-bg/strings.xml create mode 100644 Tethering/res/values-bn/strings.xml create mode 100644 Tethering/res/values-bs/strings.xml create mode 100644 Tethering/res/values-ca/strings.xml create mode 100644 Tethering/res/values-cs/strings.xml create mode 100644 Tethering/res/values-da/strings.xml create mode 100644 Tethering/res/values-de/strings.xml create mode 100644 Tethering/res/values-el/strings.xml create mode 100644 Tethering/res/values-en-rAU/strings.xml create mode 100644 Tethering/res/values-en-rCA/strings.xml create mode 100644 Tethering/res/values-en-rGB/strings.xml create mode 100644 Tethering/res/values-en-rIN/strings.xml create mode 100644 Tethering/res/values-en-rXC/strings.xml create mode 100644 Tethering/res/values-es-rUS/strings.xml create mode 100644 Tethering/res/values-es/strings.xml create mode 100644 Tethering/res/values-et/strings.xml create mode 100644 Tethering/res/values-eu/strings.xml create mode 100644 Tethering/res/values-fa/strings.xml create mode 100644 Tethering/res/values-fi/strings.xml create mode 100644 Tethering/res/values-fr-rCA/strings.xml create mode 100644 Tethering/res/values-fr/strings.xml create mode 100644 Tethering/res/values-gl/strings.xml create mode 100644 Tethering/res/values-gu/strings.xml create mode 100644 Tethering/res/values-hi/strings.xml create mode 100644 Tethering/res/values-hr/strings.xml create mode 100644 Tethering/res/values-hu/strings.xml create mode 100644 Tethering/res/values-hy/strings.xml create mode 100644 Tethering/res/values-in/strings.xml create mode 100644 Tethering/res/values-is/strings.xml create mode 100644 Tethering/res/values-it/strings.xml create mode 100644 Tethering/res/values-iw/strings.xml create mode 100644 Tethering/res/values-ja/strings.xml create mode 100644 Tethering/res/values-ka/strings.xml create mode 100644 Tethering/res/values-kk/strings.xml create mode 100644 Tethering/res/values-km/strings.xml create mode 100644 Tethering/res/values-kn/strings.xml create mode 100644 Tethering/res/values-ko/strings.xml create mode 100644 Tethering/res/values-ky/strings.xml create mode 100644 Tethering/res/values-lo/strings.xml create mode 100644 Tethering/res/values-lt/strings.xml create mode 100644 Tethering/res/values-lv/strings.xml create mode 100644 Tethering/res/values-mk/strings.xml create mode 100644 Tethering/res/values-ml/strings.xml create mode 100644 Tethering/res/values-mn/strings.xml create mode 100644 Tethering/res/values-mr/strings.xml create mode 100644 Tethering/res/values-ms/strings.xml create mode 100644 Tethering/res/values-my/strings.xml create mode 100644 Tethering/res/values-nb/strings.xml create mode 100644 Tethering/res/values-ne/strings.xml create mode 100644 Tethering/res/values-nl/strings.xml create mode 100644 Tethering/res/values-or/strings.xml create mode 100644 Tethering/res/values-pa/strings.xml create mode 100644 Tethering/res/values-pl/strings.xml create mode 100644 Tethering/res/values-pt-rBR/strings.xml create mode 100644 Tethering/res/values-pt-rPT/strings.xml create mode 100644 Tethering/res/values-pt/strings.xml create mode 100644 Tethering/res/values-ro/strings.xml create mode 100644 Tethering/res/values-ru/strings.xml create mode 100644 Tethering/res/values-si/strings.xml create mode 100644 Tethering/res/values-sk/strings.xml create mode 100644 Tethering/res/values-sl/strings.xml create mode 100644 Tethering/res/values-sq/strings.xml create mode 100644 Tethering/res/values-sr/strings.xml create mode 100644 Tethering/res/values-sv/strings.xml create mode 100644 Tethering/res/values-sw/strings.xml create mode 100644 Tethering/res/values-ta/strings.xml create mode 100644 Tethering/res/values-te/strings.xml create mode 100644 Tethering/res/values-th/strings.xml create mode 100644 Tethering/res/values-tl/strings.xml create mode 100644 Tethering/res/values-tr/strings.xml create mode 100644 Tethering/res/values-uk/strings.xml create mode 100644 Tethering/res/values-ur/strings.xml create mode 100644 Tethering/res/values-uz/strings.xml create mode 100644 Tethering/res/values-vi/strings.xml create mode 100644 Tethering/res/values-zh-rCN/strings.xml create mode 100644 Tethering/res/values-zh-rHK/strings.xml create mode 100644 Tethering/res/values-zh-rTW/strings.xml create mode 100644 Tethering/res/values-zu/strings.xml create mode 100644 Tethering/res/values/strings.xml diff --git a/Tethering/res/drawable-hdpi/stat_sys_tether_bluetooth.png b/Tethering/res/drawable-hdpi/stat_sys_tether_bluetooth.png new file mode 100644 index 0000000000000000000000000000000000000000..9451174d65d7c5477f66fcf51010c4a0144c82ab GIT binary patch literal 891 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k0wldT1B8JTQVV~v#o=I(~Nj0 zYjej-dU=M4ZThfL$V+nR8x8I$szRxWi&K_P$uKjPo;dYLS_`XNXOP+XF1 zr*r4s4&(Qh-zTM4p8Nc->fib6zaMeEUwKgH^grWV=Wil4C)k)cBh>;F&NFcyW<7f} z=Q_h<1ky-1tl+Y_|p+xrDV1v3`+Y;G2xvMInYKq2AsbS3c{rzLJnE)@}%U=|ga z6~MUT6hrcx2bQi*)eDSy68`?!68=EyxMZ^OcaOBiUdAq}d!7ji|9PM_xA#3$s#*o> znxsM#Pvc)&q3&xsqW>(Y+Z1iH(yn0Tj??QuI?lU$)AVcW>7%9lzR&)Abo0F}&Nhh# zGmcqVnm-Qp3`#-#Sn=~tsPF|rbbGlZR7EUF&}g7{?;*OR5p0-?%h%g zOn0Ist`Q~9MX8A;nfZANA(^?U42Jr~hWdtvdJ?8{fC^MW3Q`hFQmv9fs!|z@3=9o* z4J>ty3_=V|txPPfOiXkQ%&iOzCU~ALMbVI(pOTqYiCe=%k2boFyt=akR{09gog-~a#s literal 0 HcmV?d00001 diff --git a/Tethering/res/drawable-hdpi/stat_sys_tether_general.png b/Tethering/res/drawable-hdpi/stat_sys_tether_general.png new file mode 100644 index 0000000000000000000000000000000000000000..79d5756ae38ed3e6059370fd575023df68759738 GIT binary patch literal 963 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k0wldT1B8JTQk4Ey_J*>>4z&o!quZom6w@~v6b-%cB{cO5#zd)})-#l6pmu`T`X zhwSnM_c;wW%ap?UWFo(YJ$#tD{a1j_L6cX?8#is^ijIgnx~}!h#9K0s8{aH{os=sS zY%y7;`do3}?*B?_kA8jnV-w5gfJa*%9pIJ!eQf6azwgeN+kJDhh<$UY-95l%S;=Z< ziO%bHUZyU4@wP(OgzLfQ8@ho#>~W&O8h1ZOi=-w?ukK9x^K9=0uSfEAnj9>T+9KKy z)kgo#P0fD&_rjW^y-f|hzb<}GN)=izr+3D7>ZYm}AwFS`D(zSCp1XS1!T-@TtM`$G zcXm~}m>gZ_TN>9?yL|Pe*?ZeE{eEiIoA378y?9oG^S8xc6M9|V-3k<9w|T0`uJY!` z%p+10G8$|77)#f`UVQ4hce?p2?{JrxkE#BjtjZMLOa^*>%2%z-*EfWP+a9QMXHARxU9k!^%DVp*cq(xn_4{%I-khfz}#p&)7%5y_!?n&{T zxbK0kX3SphCycYpHW(T`NRZCCqE}>e;6uCvZ-Fdtg(F{Cs=VXh=k7-o&d)lPE64Ns z!mbzJGyPwq9HRlxKsTH+c};#`!PSdy8arx22vo62CQZ)~Vt)x7$D3!r6B|j-u!7Z~WwLHHlyI8?F*tBr#V>6&S22WQ% Jmvv4FO#oIHmizz! literal 0 HcmV?d00001 diff --git a/Tethering/res/drawable-hdpi/stat_sys_tether_usb.png b/Tethering/res/drawable-hdpi/stat_sys_tether_usb.png new file mode 100644 index 0000000000000000000000000000000000000000..cae1bd1b2574c898ade479aacf0e4762093f5cd2 GIT binary patch literal 909 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k0wldT1B8JTQpKm@< zdh+h^nCA4vviE0h=36}fbMDMh<$Xbm|0m0)H@~-VR7u|Ax+3!Y4F0I*i5H}WSMW@p z(R9E#PC!E_PI5xe-T?7CZ|n9?@mJPm?aI17S!v!%Z4a)H71NKj?W~B_?b&;M*REY8!Q$z+&($*Qm}%W4!ORQdo@Nc4dmdQ%R~aXs-tkjbV%Zn3V`f*DC#l8h z1@C^fWb(RIyOMUt#(68Z{LrU4EB8)A54I1=ccyd)tLFKTe!` zz;v3#eW4Ew8qDho=I9l4*Vg`3o#OibeiDPoi%rvu59sNIl(RfBTprn)$ZO-{{_Rw3 z&NoxZ{T1hI?=e}XTkt!Vz+ZL0 z^m@)$1^H8YIzlYjukyIE=+E4qrXIGxe^2{Aw^7{| zyX{$mbZw7|l4YalH-R5fZ@*f}hK5Tm6<|{9|0gA|RH0abXGPjnC}Q!>*kacg)e zQhE}oK?Y<)aDG}zd16s2gJVj5QmTSmW>IQ+eo=O@f^)EG;nv4yKy?hBu6{1-oD!M< DT$^y< literal 0 HcmV?d00001 diff --git a/Tethering/res/drawable-ldpi/stat_sys_tether_bluetooth.png b/Tethering/res/drawable-ldpi/stat_sys_tether_bluetooth.png new file mode 100644 index 0000000000000000000000000000000000000000..ffe8e8c98232649a7e1a1db66b847e7a3b1a53ed GIT binary patch literal 534 zcmeAS@N?(olHy`uVBq!ia0vp^JU}eW!3HFke0{SLNUQj3FfcGmdAc};Xq-PAw9v~XP-IT=-h=W1JWA4k zSRPMvbrj%XYYsHv2unQ8)*ZjVZLQM3mNea_1X06?me7!t`!g-uV&&iO)lIs^9Q^J~ zLHfD92afcvjop)f`qQEKBbt9S>-X-R=a^Gu%fQZ1bl0%er92?;@YQ#Z7K*$M`I0@y zdXkC>BZFU=YN^WI@SRUY+zGB z$0j$m(}yGv%xhWL_{KdUJtvs8Q!tM0-E7uMP6p=ZfsSQdpSCSp>2^p)NiJbp`_ijs z3zl{S8%Jv;i?oL4>wJHAPS@-7$(;S)E^WU1I^&s&>EuasXW#Uie0u)Qj3FfcIcdAc};Xq-PgN!Q=mQNZ=QO<0(MfJV~_ zhbzer3Rxbi>l7Ol8t*qaJan8cz~Q*S(86QIaRqG-M}@A2DgArq-gGRPy-&eIb#l** z*_+FY)7k&Uo_zK6ci1EYbIzC@cCF{PUl*R_n-=dQ=(APpE|-kTcRL3~qt_eQbr>g1 znGhs(aQVABwi1^md8GN3WOjO6LK=f@LY>Uf>z@VQZ#I}L=_C`kf?0p+hv)mBT|Vz~?!i5$6xJJz zSq_Vt_#3$I73pne|1@M$e{-L1;Fyx1l&avFo0y&&l$w}QS$Hzl2B?U^)78&qol`;+06?tDqyPW_ literal 0 HcmV?d00001 diff --git a/Tethering/res/drawable-ldpi/stat_sys_tether_usb.png b/Tethering/res/drawable-ldpi/stat_sys_tether_usb.png new file mode 100644 index 0000000000000000000000000000000000000000..65e907565ec15be0fd316350a4103423524c25d2 GIT binary patch literal 518 zcmeAS@N?(olHy`uVBq!ia0vp^JU}eW!3HFke0{SLNUQj3FfcH3dAc};Xq>-0VZRr%qd@z9!)T6QJZ?EU zK~YOQxizGyGpF*8aA(aw^W*JTmiM3Xr+cn#b76eEX09N24wJ?l z_6gf1-q*^lV4AySzg2{I!>NyJnIv8>YvABz2>tc>&+=V70asb3%-{9A_wa17fHMrw zH??XX5UB8ElVN0DpyDLEtl8%e@0F0P+&RqUN|)Fc?b&)pD8O4Nf&WY0Jw3I)^mA3; z1g82kUR(4!HbI@iZT@73wWpG9)%U2Me#q-E?bcuS1k;u;OdFIgo%w6`ME;2Q&Z5MN z96Q{4fI*~M;u=wsl30>zm0Xkxq!^403=MP*4RsBSLJSS93{0$yO?3^-tqcq%im$qj zq9HdwB{QuOw}uB^LZC>H1lbUrpH@s5Ba=>(*vG=)G0fH|yGb)dM ze?0f^Gp^Y-&(&@g^eZgt;XUnJS{C!ycv3L8?-Wx7T<=^+d zv3!11^Zf3ma$k6tf4_QTh53<$m)op5@=sXEb7_TTXasF{?=1~85T?sqG)AIG3>uGYUPZN?t06%jJEGR^fC zvqS8^s&6>5VD~-s87k^3R}&9hsz0~ui$ck-=jVa(BU<7bQQ};bnpl#VpQjL#nVZUB zsBdhjZ)m6|VLAt>Koz7QC9x#cDjB3ImBGls&`{UFQrE~J#L(2r#L~*dMAyLF%D`ZP z=h;#e4Y~O#nQ4`{H9Qn4Jqgqx1F|7FKdq!Zu_%?nF(p4KRlzN@D78GlD7#p}IoPyt S>ti#ZItEWyKbLh*2~7ZVw+i(D literal 0 HcmV?d00001 diff --git a/Tethering/res/drawable-mdpi/stat_sys_tether_general.png b/Tethering/res/drawable-mdpi/stat_sys_tether_general.png new file mode 100644 index 0000000000000000000000000000000000000000..065516185ad4896f40945ba3eb4845b1823a7d6b GIT binary patch literal 659 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_*1rX+877l!}s{b%+Ad7K3vkw8&y zArNM~bhqvgNU*HHF&#*Q^)giWEM3OHz*z3-;uvCadhV3{elmsv?e?=@2(V7inbOqv zjBAQe%L>h=rl8>a+%o!(<)tT#Kd^XAtZ2;ks1!046>_muYMd7PdvSzwZFTa=O}57K zex9|rF}@kN&2;swnr}Pzq)Ts}b@kTegtH6`C)a#Dp}K$OXZ{~Y!uNe{R_yd}VES)3 z^JIh>D=F@0q@0l?Ka(<8CAwHCrbQ* zckj&b$*;d^MJY>_~T*7?@PtCtDnx*R;D z)Z4p5K}?KM^&|U_P>zXznpzhmC}(~u)|nC_7B+3t{s~TwN(cK7d6bkryLUXP{LWnd zkGgF$i{Dqi-}&6OdAHkd(dvvKZ}o$df=j%am=Jv6>(Hy1xGWnpWz>J4a{2oAt&Yrro}iI-QAwdD=D(CV9q+`%(AV@-{H{ z9^_SIpJ%`pw}oSRgLcf~Jii={##xuwOe&Uba5)z)xA_#;YwzC?J3?0SAI-I2{r#w@ zyWQ6fw%x3XtA5`zHZDGH{iwMA%9Xyv>H^Qd4~)-u_g^`+Yxb=L-r+tTd@9Fcp7Dw< zuD^M!=BY~E5X1{vrq`Z%)m+tMu zDZhcyCR*YeQQ};bnpl#VpQjL#nVZUBsBdhjZ)m6|VLAt>Koz7QC9x#cDjB3ImBGls z&`{UFQrE~J#L(2r#L~*dMAyLF%D`ZP=h;#e4Y~O#nQ4`{H9Qn4Jqgqx1F|7FKdq!Z qu_%?nF(p4KRlzN@D78GlD7#p}IoPyt>ti#ZItEWyKbLh*2~7ahln(g- literal 0 HcmV?d00001 diff --git a/Tethering/res/drawable-xhdpi/stat_sys_tether_bluetooth.png b/Tethering/res/drawable-xhdpi/stat_sys_tether_bluetooth.png new file mode 100644 index 0000000000000000000000000000000000000000..3f57d1c76ccb139dbf08eed75e945e5896163896 GIT binary patch literal 1111 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}a}trX+877l!}s{b%+Ad7K3vk;M!Q z+(IDCcKfnz$52J2-w_pT?Lfq~iD)5S3);_%iPXS-!wCEDk&lIxT@`oK}B zxO0vA<{yGQj*fd~=qPnwyxKftatEi1^$yQHE4wR9CZ&X3cyM2Vjm5Uh+*w6?Q?-EY zkF5d|zbZ$b-8A#vacA>QhL>$)PcM0H;(GP>p8cz~->X_}@MF{c=m?8j>kH0t9z1;Y zzM1nShFx#wJ$Rqvq_p^dk&#%FQes9r=b6p2oVr!(B}JoWw%dKMGIrt0;pGqz{=>QF ztmGyYmrHlbIb;@1Z}VUjIpOQ`Sx$1B>kOcZ2$_9o@G_0IFG;se*zcYnVdF!{}wuBv@OCJ3tpM=e^1vm^Q(96T+L=!{q1VycFCj7 zzEW&&n>`NdRoyw~d2f-z6&dg78ypAvec8`F(A4_7bA#KTtz4%xcZDAb|FhfEF?->e%n+qi zC9^Q2TGEHng(=+ftl+0_<-0{3Ka(;8X1Hbnp&AyTA7&W8kk!d7)_EoKfnz$52J2-w_pT?Lfr0s+r;B4q#NoX&_U4JYiyW`tygR~enFGs5 z78chB4K5QpR{Rhud6^@S8>DctNn=v_1I7sgrU43p(i*W0$2K{3arifQNKH=IQT}{- zQu_CIcdOUSYVNbU{O0(a_5WuUpF5NALHxdSuiv7K`}>|=*nVl>U)>6mU$;&-nO}On zBy;bzX!~BquiGA6@sH#BceG9=d5V`2(}t7U_dZ#_{^QB!vhQK_zQ+>w2HYMh@~7v2 zp2m6Yc*^r#=C-+t$!-rb^LNdwk=A!T_HRYY{x7qPdpF!!WBSSMWzY4Rn~z=z2p{?X zIR9MdjM>s!iKeO-58jD6VwwN*dxhv?F0ay~!JZa}7u~+%5PH_?sn?@>L5}{lZ|Wp( z+>MCdq28PHBwz8}_rggAfsXNe-}~GybAG+k`c_s^;j!Pb3!31d+ZY=CU37ZwnHNh9Bfg%FlQDeRB0cRZPt>>nw}j#q>u>MtyRxwH{MGxn zoiCm^I-6NOqiOf6IhC7KXLOd!qTF>-3!{E7pb59 zV&AImR(HBRSC#5kC113d*_r;==s;n#=+bzb+g+|3KJ=~>3tefrL1D(u>*Bk3E$;rQ ze0Yy-nN(w3=$l2-^KwLQ1KmFF*MI~)1!N;FA|k1e});nb<+ePL5~ z9E~>povHo1d9&W?zf29GW|}Y9c}^srb6C#m$uRGujK%Rvr(aH!j~28Y-lNVkwaGW^ zr{Jo(g2hGMr*^(hexrN0VByAsg>$`h=dbIwH1#mQ5+N|5yfI|5(}BkZmspJJo(VQG zoKR-sw+gkHRBV&*bUNdG#f7gn$~T^R%plXG_{`&hB>%}q50)aiPdyi=SPC6q=;d_i zo$^Mvs+iOH+6~WJmVNGEnYY}2vgYl$FMI*lZtVCvr+9`ELu137-~RI^GfJ{&3kd#T zn5(MsJaP$(k(AY?mbI-Rv+{H5BF5|S#IvRWe9bDua=Mp`osUrLK`dh@q*KiKUf^ ziLQaUm4U$o&$Fc{8glbfGSez?Yj`M9dJ?EX24q8Uep*R+Vo@rCV@iHfs)Ac)QEGX9 aQFgI{bFgXQ*2iW*bqt=aelF{r5}E*%!!$ks literal 0 HcmV?d00001 diff --git a/Tethering/res/drawable-xhdpi/stat_sys_tether_usb.png b/Tethering/res/drawable-xhdpi/stat_sys_tether_usb.png new file mode 100644 index 0000000000000000000000000000000000000000..36afe485b5bb65e88aa6b76926214da656dc804a GIT binary patch literal 1182 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}a}trX+877l!}s{b%+Ad7K3vk;M!Q z+(IDCcKfnz$52J2-w_pT?Lfq}Wn)5S3);_%iPz8SHuBFF2C-&cEjNf~%7 z65krq6%c1PiJw>vKmwJPk`xcl82S1BKtq}VAm~8m^V^vMR?a^? zt?Yt)@9ik%QlT?b7{g~IPKp1)WpV89nvbmfLc%_CvvflBnJaewkEyB-&Nivp6}~QT z1J?m9*6bVxamxdWd{_RuF1WLAL#*pWt`jF1PrSTz^UMC{b6EdRxcX2z$Yf(C^Mp0) z@0Z1&=Xk>LB=@6EcB1@_BiuIUS#PLLk!T6{ZgQ+ejZ>>=TE#Sunj`6RQn#?GY!2q0 zF3jl~bdBw1M-=VbS0aUEbjGfB8X^O?mR zll7N3Y*2bGFi~bgSNj6T{gy>N{x|#^_nZv=dYgrlNix4+M%SD!%QPu}50z^NxatEp zOkB69OL-6L35|5g`-;0nQl9)dcl*vlZ@!Y*y?5TQ3(Z_{+Dy6o+*ebTGeNyjo6 z%YRR>&yoLr`0nOt>jQgc%+60V(l7gcroU<-oBr+BtI`_0{@#=Po|{y5@09nW6Wiv# zpE$StJp270v%Y_IocG1j@bknebKmm{yx#lwwn=kl&spu>mXOl8nuTfay6?rJ9y?^0hFoWOY{AMoiF6~(eQC+(2-x8CYJ4B-fr1z@%B{g&s&=wZTZ9fdbVG7wVRUJ4ZXi@?ZDjy4FEcSOF)=O}HJktd18+%0 zK~#90?VR0p8Zi)szv1K^cMx}A(g7XB9c0r19T<0z4uUHP?tl)w?!f5)D!6%&;1G5f z7A&oXW8XLLkM`=bcF>FjMwFx}Fay5xakT-Kz}(E<01QCV9C#>Z%bzyDy_u~{(mn8} zv?GVw027l`19zp}IMtI$QfU{CwHcLm06dj8a75$cfXuAIF{I*vz&9ipfWR*#H-Nw= zBv*jIA0&5xz!xN!fWQwVw}4;=>9MGX(lco@vs2eds(|15-8AKL1WB6g|7~Uq;AaUh zE&%~6FOa+f8sh|#S3uwdl4n5R0Frk=P#DrR0D^*$QUiJdz7guVsWk>(;+$xw2r1Vy zLFu&EYjYxL-RGtYJ>9q5)BBm{DAfphjA;P?W>y1J;BTV#_w@?YEyrhsQI=MTK(BQW zA7a}#lImkOp6O{J>89lw%S8QX`b*f>T48neEJ$YnnqBe_c&}0d+N$d4$g0)AX+T}z zv&5W+zfA2mAjM8C8U~I7swLgFoUb$h94Q|lDXjOOgmeT@Bi_Vwvy7e_$_Yr?`{+OD zIwIas^u&0KuN%w{n;k@LxbX-Gc^LIe4mRa|qV!HCV>~l;pzk4p~ z!RsrwRSfVe3I*vQN6^`D0OIx2c>mxU3eYnxaMYcW{CB)4qygqyi;m}ZP88#PRm<@k zNpAyq-pOmKu~i$SL9+Orzl?3qJ38MtS>0+{5|a0FhtyrIyxMwD+iUTjf|*nhQtFax z+iF9ql4}>FDq$a*{t~q_RMCELNT~qLJBI(skK{7}NdrSlHBo%f!NMUSJ@}fa{e6vr z+m_=sIaXH_17GOOz#STv!Lb_5PTw9TG|Dx-1$DWG7f$IQN3=a4Q zR1^;Q22>ml_yyzw4mjOuFKOZd4mbsLs8Vpi?<=;GLK;&94!{sg&lgJ}efd$+gWD10 z0S-6?t&$@P7!>(ur~-rhz4X0`$*gn;>xL2U>OCXosM0Hbq)X6R%4 QL;wH)07*qoM6N<$f-lR^CIA2c literal 0 HcmV?d00001 diff --git a/Tethering/res/drawable-xxhdpi/stat_sys_tether_general.png b/Tethering/res/drawable-xxhdpi/stat_sys_tether_general.png new file mode 100644 index 0000000000000000000000000000000000000000..5c656012e61570308c4565423540d975b0daf689 GIT binary patch literal 1219 zcmV;!1U&nRP)dbVG7wVRUJ4ZXi@?ZDjy4FEcSOF)=O}HJktd1RP03 zK~#90?VQndn=llHf93Q&=>*XR^#-96)Ek6s&<#p9$O!HP^#&yyBpZ+kY9{#mfJiYG zNEZoVH+<*tV7TU9{RknhBoquJGXUQKe8b132CxTED5WX@Pyj%(0Pth6u+EPfz*Z?$ zDw10Ozm|^Z45sSE}P=oWbH@EOCklY#{voV)IJCb@XVJt#v<1;$31uB_Pm;s^6m<5U@ z(1B#WlrVkR+_O8oNgGYn=j-*ax)>`(F^GkNMDFJEn<<_w2_013A7VB^17!JAZ{}`@(>vlNDG~$ zY;#itd;~QEVDn+)Xehp4gDRoZ5NZI~fH(jEb1XBqA!GvEs{5lakfa7_fvt_LQp)Mi z*o3BewZQf$roKRuK2S*e^E_5_`WV}gHc1lekp{NMdJx#=hpy!lYBe1IeA&>2vFHdso-Wcke^Zb`X&tvamg~SP(1}-!;8iN}0 z3}i8Q#Bn9G#0x*z^cQlSK($W6bDemd#OYw22JB#+NbX==iV#^Ka$U^gWL*yBNxpQU zePQASbzO?33-5vpF3D_})JdBgp2?!#mUUUUCneVp3wROK%bh69EUDkQ1ZuhXkDDkq zN&5LeGjpMz$5CuD|EXuoU(^SdG@2miz8aTC4PbL+GXLk2Pd*pg`PtNj1~002ovPDHLkV1g+19F_n8 literal 0 HcmV?d00001 diff --git a/Tethering/res/drawable-xxhdpi/stat_sys_tether_usb.png b/Tethering/res/drawable-xxhdpi/stat_sys_tether_usb.png new file mode 100644 index 0000000000000000000000000000000000000000..28b4b5438e55af7320e0a0e50a8a438de23db9f2 GIT binary patch literal 1002 zcmVdbVG7wVRUJ4ZXi@?ZDjy4FEcSOF)=O}HJktd14Btf zK~#90?VQnd+AtJ`zsu=+VS=U;gicU0LD?X5gJgm-0-2yNLCFSWf|3bJCTKjt?So}X zIkx54vaekHos)AC%a(rsxw@7tf#6Wp8E^-D;c`iUEwB`k1AqXi>Js>7CM!M?U@js% zQPnx{!&*^R@*ixRFYmbERU8@2HmRaIPvcEgBTZC0U=NqWP{2B)^^lR=e?QPoArMq@W9Ax-e>ipDGn{bFAO zQc(Y)&b6w#bi)Im{zcU`YTZ-N${O6n+y1TX8*|hZw6X>_6`L_fU4iSH)xF8sj5+FR zq0WP*H&Zo#>{Xj}&o*hppo~{lU8(9-1*2xM8I#e~%IYJq${Y0-(UC4MT`xVqkd9Th zzP0&FH7LV70K9aAPRX5gMxYCb!ysVT-O*bHgFFldc^C}xFc{=vFvvp;20Z|ioZ}N$ zW{UMV=&OjV%TCbrRqFRn)Z}2j(|+wc$(>{NaRjRfD%?+0gsSd<&#kB_!775*9bMBH zs|YIH?2w06*Z|Q6CI(gA{B(|m))*f1QDb6|R$TpPdQAwdB52XkHI1nwF#C6T?0EUSvPZKI}E>_#(FN6u_29;Y-}_J=0vb{PgM04 z&&LEVm}G=u|Ly@>as7PH5Jl%Cz;xsuwR3l1ASL^fx5EgIpU9b#Cd*4UDk1e?nyvKy Y0bAvkc^|Z@`Tzg`07*qoM6N<$g4MLdtpET3 literal 0 HcmV?d00001 diff --git a/Tethering/res/values-af/strings.xml b/Tethering/res/values-af/strings.xml new file mode 100644 index 0000000000..1258805378 --- /dev/null +++ b/Tethering/res/values-af/strings.xml @@ -0,0 +1,8 @@ + + + "Verbinding of Wi-Fi-warmkol aktief" + "Tik om op te stel." + "Verbinding is gedeaktiveer" + "Kontak jou administrateur vir besonderhede" + diff --git a/Tethering/res/values-am/strings.xml b/Tethering/res/values-am/strings.xml new file mode 100644 index 0000000000..9c36192257 --- /dev/null +++ b/Tethering/res/values-am/strings.xml @@ -0,0 +1,8 @@ + + + "መሰካት ወይም ገባሪ ድረስ ነጥብ" + "ለማዋቀር መታ ያድርጉ።" + "እንደ ሞደም መሰካት ተሰናክሏል" + "ለዝርዝሮች የእርስዎን አስተዳዳሪ ያነጋግሩ" + diff --git a/Tethering/res/values-ar/strings.xml b/Tethering/res/values-ar/strings.xml new file mode 100644 index 0000000000..9f84ce4090 --- /dev/null +++ b/Tethering/res/values-ar/strings.xml @@ -0,0 +1,8 @@ + + + "النطاق أو نقطة الاتصال نشطة" + "انقر للإعداد." + "تم إيقاف التوصيل" + "اتصل بالمشرف للحصول على التفاصيل" + diff --git a/Tethering/res/values-as/strings.xml b/Tethering/res/values-as/strings.xml new file mode 100644 index 0000000000..8855822e7c --- /dev/null +++ b/Tethering/res/values-as/strings.xml @@ -0,0 +1,8 @@ + + + "টেডাৰিং বা হটস্প\'ট সক্ৰিয় অৱস্থাত আছে" + "ছেট আপ কৰিবলৈ টিপক।" + "টেডাৰিং অক্ষম কৰি থোৱা হৈছে" + "সবিশেষ জানিবলৈ আপোনাৰ প্ৰশাসকৰ সৈতে যোগাযোগ কৰক" + diff --git a/Tethering/res/values-az/strings.xml b/Tethering/res/values-az/strings.xml new file mode 100644 index 0000000000..eba50eb636 --- /dev/null +++ b/Tethering/res/values-az/strings.xml @@ -0,0 +1,8 @@ + + + "Tezerinq və ya hotspot aktivdir" + "Quraşdırmaq üçün tıklayın." + "Birləşmə deaktivdir" + "Məlumat üçün adminlə əlaqə saxlayın" + diff --git a/Tethering/res/values-b+sr+Latn/strings.xml b/Tethering/res/values-b+sr+Latn/strings.xml new file mode 100644 index 0000000000..5b0e488ba5 --- /dev/null +++ b/Tethering/res/values-b+sr+Latn/strings.xml @@ -0,0 +1,8 @@ + + + "Aktivno povezivanje sa internetom preko mobilnog uređaja ili hotspot" + "Dodirnite da biste podesili." + "Privezivanje je onemogućeno" + "Potražite detalje od administratora" + diff --git a/Tethering/res/values-be/strings.xml b/Tethering/res/values-be/strings.xml new file mode 100644 index 0000000000..5966c7155e --- /dev/null +++ b/Tethering/res/values-be/strings.xml @@ -0,0 +1,8 @@ + + + "USB-мадэм або хот-спот Wi-Fi актыўныя" + "Дакраніцеся, каб наладзіць." + "Рэжым мадэма адключаны" + "Звярніцеся да адміністратара па падрабязную інфармацыю" + diff --git a/Tethering/res/values-bg/strings.xml b/Tethering/res/values-bg/strings.xml new file mode 100644 index 0000000000..ed58d7311a --- /dev/null +++ b/Tethering/res/values-bg/strings.xml @@ -0,0 +1,8 @@ + + + "Има активна споделена връзка или безжична точка за достъп" + "Докоснете, за да настроите." + "Функцията за тетъринг е деактивирана" + "Свържете се с администратора си за подробности" + diff --git a/Tethering/res/values-bn/strings.xml b/Tethering/res/values-bn/strings.xml new file mode 100644 index 0000000000..8d9880aa9a --- /dev/null +++ b/Tethering/res/values-bn/strings.xml @@ -0,0 +1,8 @@ + + + "টিথারিং বা হটস্পট সক্রিয় আছে" + "সেট-আপ করার জন্য আলতো চাপুন৷" + "টিথারিং অক্ষম করা আছে" + "বিশদ বিবরণের জন্য প্রশাসকের সাথে যোগাযোগ করুন" + diff --git a/Tethering/res/values-bs/strings.xml b/Tethering/res/values-bs/strings.xml new file mode 100644 index 0000000000..2361b9dd38 --- /dev/null +++ b/Tethering/res/values-bs/strings.xml @@ -0,0 +1,8 @@ + + + "Uređaj dijeli vezu ili djeluje kao pristupna tačka" + "Dodirnite za postavke" + "Povezivanje putem mobitela je onemogućeno" + "Kontaktirajte svog administratora za dodatne detalje" + diff --git a/Tethering/res/values-ca/strings.xml b/Tethering/res/values-ca/strings.xml new file mode 100644 index 0000000000..6752b519e2 --- /dev/null +++ b/Tethering/res/values-ca/strings.xml @@ -0,0 +1,8 @@ + + + "Compartició de xarxa o punt d\'accés Wi-Fi activat" + "Toca per configurar." + "La compartició de xarxa està desactivada" + "Contacta amb el teu administrador per obtenir més informació" + diff --git a/Tethering/res/values-cs/strings.xml b/Tethering/res/values-cs/strings.xml new file mode 100644 index 0000000000..5fdd53adf1 --- /dev/null +++ b/Tethering/res/values-cs/strings.xml @@ -0,0 +1,8 @@ + + + "Sdílené připojení nebo hotspot je aktivní." + "Klepnutím zahájíte nastavení." + "Tethering je zakázán" + "O podrobnosti požádejte administrátora" + diff --git a/Tethering/res/values-da/strings.xml b/Tethering/res/values-da/strings.xml new file mode 100644 index 0000000000..2775dfa551 --- /dev/null +++ b/Tethering/res/values-da/strings.xml @@ -0,0 +1,8 @@ + + + "Netdeling eller hotspot er aktivt" + "Tryk for at konfigurere" + "Netdeling er deaktiveret" + "Kontakt din administrator for at få oplysninger" + diff --git a/Tethering/res/values-de/strings.xml b/Tethering/res/values-de/strings.xml new file mode 100644 index 0000000000..9046cd5e11 --- /dev/null +++ b/Tethering/res/values-de/strings.xml @@ -0,0 +1,8 @@ + + + "Tethering oder Hotspot aktiv" + "Zum Einrichten tippen." + "Tethering ist deaktiviert" + "Bitte wende dich für weitere Informationen an den Administrator" + diff --git a/Tethering/res/values-el/strings.xml b/Tethering/res/values-el/strings.xml new file mode 100644 index 0000000000..3b9f53733b --- /dev/null +++ b/Tethering/res/values-el/strings.xml @@ -0,0 +1,8 @@ + + + "Πρόσδεση ή σύνδεση σημείου πρόσβασης ενεργή" + "Πατήστε για ρύθμιση." + "Η σύνδεση είναι απενεργοποιημένη" + "Επικοινωνήστε με τον διαχειριστή σας για λεπτομέρειες" + diff --git a/Tethering/res/values-en-rAU/strings.xml b/Tethering/res/values-en-rAU/strings.xml new file mode 100644 index 0000000000..56b88a5fb3 --- /dev/null +++ b/Tethering/res/values-en-rAU/strings.xml @@ -0,0 +1,8 @@ + + + "Tethering or hotspot active" + "Tap to set up." + "Tethering is disabled" + "Contact your admin for details" + diff --git a/Tethering/res/values-en-rCA/strings.xml b/Tethering/res/values-en-rCA/strings.xml new file mode 100644 index 0000000000..56b88a5fb3 --- /dev/null +++ b/Tethering/res/values-en-rCA/strings.xml @@ -0,0 +1,8 @@ + + + "Tethering or hotspot active" + "Tap to set up." + "Tethering is disabled" + "Contact your admin for details" + diff --git a/Tethering/res/values-en-rGB/strings.xml b/Tethering/res/values-en-rGB/strings.xml new file mode 100644 index 0000000000..56b88a5fb3 --- /dev/null +++ b/Tethering/res/values-en-rGB/strings.xml @@ -0,0 +1,8 @@ + + + "Tethering or hotspot active" + "Tap to set up." + "Tethering is disabled" + "Contact your admin for details" + diff --git a/Tethering/res/values-en-rIN/strings.xml b/Tethering/res/values-en-rIN/strings.xml new file mode 100644 index 0000000000..56b88a5fb3 --- /dev/null +++ b/Tethering/res/values-en-rIN/strings.xml @@ -0,0 +1,8 @@ + + + "Tethering or hotspot active" + "Tap to set up." + "Tethering is disabled" + "Contact your admin for details" + diff --git a/Tethering/res/values-en-rXC/strings.xml b/Tethering/res/values-en-rXC/strings.xml new file mode 100644 index 0000000000..7f47fc89d2 --- /dev/null +++ b/Tethering/res/values-en-rXC/strings.xml @@ -0,0 +1,8 @@ + + + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‏‎‏‎‏‎‏‏‎‏‎‎‏‏‎‏‏‎‏‏‏‎‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‎‏‏‎‎‎‏‏‎‏‎‏‏‏‎‏‎‎‏‎Tethering or hotspot active‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‎‏‎‏‎‏‎‏‎‎‎‏‏‏‎‏‏‏‏‏‎‎‎‎‏‏‏‏‎‎‎‎‏‏‏‏‎‎‎‏‏‏‎‏‏‏‏‎‎‏‎‏‎‎‎‏‎Tap to set up.‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‏‏‎‏‎‏‎‎‏‏‎‎‏‎‏‎‎‎‏‏‏‏‎‏‏‏‏‏‏‎‏‏‏‏‎‏‎‎‎‏‏‏‎‎‎‏‎‎‏‎‏‎‏‏‎Tethering is disabled‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‎‏‏‎‏‏‏‎‎‏‎‏‏‎‏‏‎‏‎‏‎‏‎‎‏‏‎‎‎‎‎‎‎‏‎‎‏‎‏‏‏‎‏‎‎‎‏‏‎‎‎‎‏‏‏‏‎Contact your admin for details‎‏‎‎‏‎" + diff --git a/Tethering/res/values-es-rUS/strings.xml b/Tethering/res/values-es-rUS/strings.xml new file mode 100644 index 0000000000..e4618b8cec --- /dev/null +++ b/Tethering/res/values-es-rUS/strings.xml @@ -0,0 +1,8 @@ + + + "Anclaje a red o zona activa conectados" + "Presiona para configurar." + "Se inhabilitó la conexión mediante dispositivo portátil" + "Para obtener más información, comunícate con el administrador" + diff --git a/Tethering/res/values-es/strings.xml b/Tethering/res/values-es/strings.xml new file mode 100644 index 0000000000..8dc1575ce8 --- /dev/null +++ b/Tethering/res/values-es/strings.xml @@ -0,0 +1,8 @@ + + + "Compartir conexión/Zona Wi-Fi activada" + "Toca para configurar." + "La conexión compartida está inhabilitada" + "Ponte en contacto con el administrador para obtener más información" + diff --git a/Tethering/res/values-et/strings.xml b/Tethering/res/values-et/strings.xml new file mode 100644 index 0000000000..872c8a74cc --- /dev/null +++ b/Tethering/res/values-et/strings.xml @@ -0,0 +1,8 @@ + + + "Jagamine või kuumkoht on aktiivne" + "Puudutage seadistamiseks." + "Jagamine on keelatud" + "Lisateabe saamiseks võtke ühendust oma administraatoriga" + diff --git a/Tethering/res/values-eu/strings.xml b/Tethering/res/values-eu/strings.xml new file mode 100644 index 0000000000..6c4605e616 --- /dev/null +++ b/Tethering/res/values-eu/strings.xml @@ -0,0 +1,8 @@ + + + "Konexioa partekatzea edo sare publikoa aktibo" + "Sakatu konfiguratzeko." + "Desgaituta dago konexioa partekatzeko aukera" + "Xehetasunak lortzeko, jarri administratzailearekin harremanetan" + diff --git a/Tethering/res/values-fa/strings.xml b/Tethering/res/values-fa/strings.xml new file mode 100644 index 0000000000..bc2ee23609 --- /dev/null +++ b/Tethering/res/values-fa/strings.xml @@ -0,0 +1,8 @@ + + + "اشتراک‌گذاری اینترنت یا نقطه اتصال فعال" + "برای راه‌اندازی ضربه بزنید." + "اشتراک‌گذاری اینترنت غیرفعال است" + "برای جزئیات، با سرپرستتان تماس بگیرید" + diff --git a/Tethering/res/values-fi/strings.xml b/Tethering/res/values-fi/strings.xml new file mode 100644 index 0000000000..ff0fca6502 --- /dev/null +++ b/Tethering/res/values-fi/strings.xml @@ -0,0 +1,8 @@ + + + "Internetin jakaminen tai yhteyspiste käytössä" + "Määritä napauttamalla." + "Yhteyden jakaminen poistettu käytöstä" + "Kysy lisätietoja järjestelmänvalvojalta." + diff --git a/Tethering/res/values-fr-rCA/strings.xml b/Tethering/res/values-fr-rCA/strings.xml new file mode 100644 index 0000000000..1f5df0ee0c --- /dev/null +++ b/Tethering/res/values-fr-rCA/strings.xml @@ -0,0 +1,8 @@ + + + "Partage de connexion ou point d\'accès sans fil activé" + "Touchez pour configurer." + "Le partage de connexion est désactivé" + "Communiquez avec votre administrateur pour obtenir plus de détails" + diff --git a/Tethering/res/values-fr/strings.xml b/Tethering/res/values-fr/strings.xml new file mode 100644 index 0000000000..daf7c9d830 --- /dev/null +++ b/Tethering/res/values-fr/strings.xml @@ -0,0 +1,8 @@ + + + "Partage de connexion ou point d\'accès sans fil activé" + "Appuyez ici pour configurer." + "Le partage de connexion est désactivé" + "Pour en savoir plus, contactez votre administrateur" + diff --git a/Tethering/res/values-gl/strings.xml b/Tethering/res/values-gl/strings.xml new file mode 100644 index 0000000000..0d16a1de09 --- /dev/null +++ b/Tethering/res/values-gl/strings.xml @@ -0,0 +1,8 @@ + + + "Conexión compartida ou zona wifi activada" + "Tocar para configurar." + "A conexión compartida está desactivada" + "Contacta co administrador para obter información" + diff --git a/Tethering/res/values-gu/strings.xml b/Tethering/res/values-gu/strings.xml new file mode 100644 index 0000000000..9d6b02f85f --- /dev/null +++ b/Tethering/res/values-gu/strings.xml @@ -0,0 +1,8 @@ + + + "ટિથરિંગ અથવા હૉટસ્પૉટ સક્રિય" + "સેટ કરવા માટે ટૅપ કરો." + "ટિથરિંગ અક્ષમ કરેલ છે" + "વિગતો માટે તમારા વ્યવસ્થાપકનો સંપર્ક કરો" + diff --git a/Tethering/res/values-hi/strings.xml b/Tethering/res/values-hi/strings.xml new file mode 100644 index 0000000000..9c29d9a8f9 --- /dev/null +++ b/Tethering/res/values-hi/strings.xml @@ -0,0 +1,8 @@ + + + "टेदरिंग या हॉटस्‍पॉट सक्रिय" + "सेट करने के लिए टैप करें." + "टेदरिंग अक्षम है" + "जानकारी के लिए अपने एडमिन से संपर्क करें" + diff --git a/Tethering/res/values-hr/strings.xml b/Tethering/res/values-hr/strings.xml new file mode 100644 index 0000000000..d0d25bb755 --- /dev/null +++ b/Tethering/res/values-hr/strings.xml @@ -0,0 +1,8 @@ + + + "Ograničenje ili aktivan hotspot" + "Dodirnite da biste postavili." + "Modemsko je povezivanje onemogućeno" + "Obratite se administratoru da biste saznali pojedinosti" + diff --git a/Tethering/res/values-hu/strings.xml b/Tethering/res/values-hu/strings.xml new file mode 100644 index 0000000000..3129659923 --- /dev/null +++ b/Tethering/res/values-hu/strings.xml @@ -0,0 +1,8 @@ + + + "Megosztás vagy aktív hotspot" + "Koppintson a beállításhoz." + "Az internetmegosztás le van tiltva" + "A részletekért forduljon rendszergazdájához" + diff --git a/Tethering/res/values-hy/strings.xml b/Tethering/res/values-hy/strings.xml new file mode 100644 index 0000000000..8ba6435fd5 --- /dev/null +++ b/Tethering/res/values-hy/strings.xml @@ -0,0 +1,8 @@ + + + "Մոդեմի ռեժիմը միացված է" + "Հպեք՝ կարգավորելու համար:" + "Մոդեմի ռեժիմն անջատված է" + "Մանրամասների համար դիմեք ձեր ադմինիստրատորին" + diff --git a/Tethering/res/values-in/strings.xml b/Tethering/res/values-in/strings.xml new file mode 100644 index 0000000000..1e093ab237 --- /dev/null +++ b/Tethering/res/values-in/strings.xml @@ -0,0 +1,8 @@ + + + "Tethering (Penambatan) atau hotspot aktif" + "Ketuk untuk menyiapkan." + "Tethering dinonaktifkan" + "Hubungi admin untuk mengetahui detailnya" + diff --git a/Tethering/res/values-is/strings.xml b/Tethering/res/values-is/strings.xml new file mode 100644 index 0000000000..f5769d5344 --- /dev/null +++ b/Tethering/res/values-is/strings.xml @@ -0,0 +1,8 @@ + + + "Kveikt á tjóðrun eða aðgangsstað" + "Ýttu til að setja upp." + "Slökkt er á tjóðrun" + "Hafðu samband við kerfisstjórann til að fá upplýsingar" + diff --git a/Tethering/res/values-it/strings.xml b/Tethering/res/values-it/strings.xml new file mode 100644 index 0000000000..e0b3724325 --- /dev/null +++ b/Tethering/res/values-it/strings.xml @@ -0,0 +1,8 @@ + + + "Tethering oppure hotspot attivo" + "Tocca per impostare." + "Tethering disattivato" + "Contatta il tuo amministratore per avere informazioni dettagliate" + diff --git a/Tethering/res/values-iw/strings.xml b/Tethering/res/values-iw/strings.xml new file mode 100644 index 0000000000..c002c44b23 --- /dev/null +++ b/Tethering/res/values-iw/strings.xml @@ -0,0 +1,8 @@ + + + "שיתוף אינטרנט פעיל" + "הקש כדי להגדיר." + "שיתוף האינטרנט בין ניידים מושבת" + "לפרטים, יש לפנות למנהל המערכת" + diff --git a/Tethering/res/values-ja/strings.xml b/Tethering/res/values-ja/strings.xml new file mode 100644 index 0000000000..314bde00df --- /dev/null +++ b/Tethering/res/values-ja/strings.xml @@ -0,0 +1,8 @@ + + + "テザリングまたはアクセスポイントが有効です" + "タップしてセットアップします。" + "テザリングは無効に設定されています" + "詳しくは、管理者にお問い合わせください" + diff --git a/Tethering/res/values-ka/strings.xml b/Tethering/res/values-ka/strings.xml new file mode 100644 index 0000000000..7bbd81d343 --- /dev/null +++ b/Tethering/res/values-ka/strings.xml @@ -0,0 +1,8 @@ + + + "ტეტერინგი ან უსადენო ქსელი აქტიურია" + "შეეხეთ დასაყენებლად." + "ტეტერინგი გათიშულია" + "დამატებითი ინფორმაციისთვის დაუკავშირდით თქვენს ადმინისტრატორს" + diff --git a/Tethering/res/values-kk/strings.xml b/Tethering/res/values-kk/strings.xml new file mode 100644 index 0000000000..7fd87a1596 --- /dev/null +++ b/Tethering/res/values-kk/strings.xml @@ -0,0 +1,8 @@ + + + "Тетеринг немесе хотспот қосулы" + "Реттеу үшін түртіңіз." + "Тетеринг өшірілді" + "Мәліметтерді әкімшіден алыңыз" + diff --git a/Tethering/res/values-km/strings.xml b/Tethering/res/values-km/strings.xml new file mode 100644 index 0000000000..2f85224679 --- /dev/null +++ b/Tethering/res/values-km/strings.xml @@ -0,0 +1,8 @@ + + + "ភ្ជាប់ ឬ​ហតស្ពត​សកម្ម" + "ប៉ះដើម្បីកំណត់" + "ការភ្ជាប់​ត្រូវបានបិទ" + "ទាក់ទងអ្នកគ្រប់គ្រង​របស់អ្នកសម្រាប់​ព័ត៌មានលម្អិត" + diff --git a/Tethering/res/values-kn/strings.xml b/Tethering/res/values-kn/strings.xml new file mode 100644 index 0000000000..f11a83ea40 --- /dev/null +++ b/Tethering/res/values-kn/strings.xml @@ -0,0 +1,8 @@ + + + "ಟೆಥರಿಂಗ್ ಅಥವಾ ಹಾಟ್‌ಸ್ಪಾಟ್ ಸಕ್ರಿಯವಾಗಿದೆ" + "ಹೊಂದಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ." + "ಟೆಥರಿಂಗ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ" + "ವಿವರಗಳಿಗಾಗಿ ನಿಮ್ಮ ನಿರ್ವಾಹಕರನ್ನು ಸಂಪರ್ಕಿಸಿ" + diff --git a/Tethering/res/values-ko/strings.xml b/Tethering/res/values-ko/strings.xml new file mode 100644 index 0000000000..57f24f5b1a --- /dev/null +++ b/Tethering/res/values-ko/strings.xml @@ -0,0 +1,8 @@ + + + "테더링 또는 핫스팟 사용" + "설정하려면 탭하세요." + "테더링이 사용 중지됨" + "자세한 정보는 관리자에게 문의하세요." + diff --git a/Tethering/res/values-ky/strings.xml b/Tethering/res/values-ky/strings.xml new file mode 100644 index 0000000000..79854859d4 --- /dev/null +++ b/Tethering/res/values-ky/strings.xml @@ -0,0 +1,8 @@ + + + "Жалгаштыруу же хотспот жандырылган" + "Жөндөө үчүн таптап коюңуз." + "Жалгаштыруу функциясы өчүрүлгөн" + "Кеңири маалымат үчүн администраторуңузга кайрылыңыз" + diff --git a/Tethering/res/values-lo/strings.xml b/Tethering/res/values-lo/strings.xml new file mode 100644 index 0000000000..78f1585f60 --- /dev/null +++ b/Tethering/res/values-lo/strings.xml @@ -0,0 +1,8 @@ + + + "ເປີດ​ການ​ປ່ອຍ​ສັນຍານ ຫຼື​ຮັອດສະປອດ​ແລ້ວ" + "ແຕະເພື່ອຕັ້ງຄ່າ." + "ການປ່ອຍສັນຍານຖືກປິດໄວ້" + "ຕິດຕໍ່ຜູ້ເບິ່ງແຍງລະບົບສຳລັບລາຍລະອຽດ" + diff --git a/Tethering/res/values-lt/strings.xml b/Tethering/res/values-lt/strings.xml new file mode 100644 index 0000000000..ebff8ac9d1 --- /dev/null +++ b/Tethering/res/values-lt/strings.xml @@ -0,0 +1,8 @@ + + + "Susietas ar aktyvus" + "Palieskite, kad nustatytumėte." + "Įrenginio kaip modemo naudojimas išjungtas" + "Jei reikia išsamios informacijos, susisiekite su administratoriumi" + diff --git a/Tethering/res/values-lv/strings.xml b/Tethering/res/values-lv/strings.xml new file mode 100644 index 0000000000..54d0048b52 --- /dev/null +++ b/Tethering/res/values-lv/strings.xml @@ -0,0 +1,8 @@ + + + "Piesaiste vai tīklājs ir aktīvs." + "Pieskarieties, lai iestatītu." + "Piesaiste ir atspējota" + "Lai iegūtu detalizētu informāciju, sazinieties ar savu administratoru." + diff --git a/Tethering/res/values-mk/strings.xml b/Tethering/res/values-mk/strings.xml new file mode 100644 index 0000000000..0fab8aa476 --- /dev/null +++ b/Tethering/res/values-mk/strings.xml @@ -0,0 +1,8 @@ + + + "Поврзувањето или точката на пристап се активни" + "Допрете за поставување." + "Врзувањето е оневозможено" + "Контактирајте со администраторот за детали" + diff --git a/Tethering/res/values-ml/strings.xml b/Tethering/res/values-ml/strings.xml new file mode 100644 index 0000000000..fd7e556e38 --- /dev/null +++ b/Tethering/res/values-ml/strings.xml @@ -0,0 +1,8 @@ + + + "ടെതറിംഗ് അല്ലെങ്കിൽ ഹോട്ട്സ്‌പോട്ട് സജീവമാണ്" + "സജ്ജമാക്കാൻ ടാപ്പുചെയ്യുക." + "ടെതറിംഗ് പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു" + "വിശദവിവരങ്ങൾക്ക് നിങ്ങളുടെ അഡ്മിനെ ബന്ധപ്പെടുക" + diff --git a/Tethering/res/values-mn/strings.xml b/Tethering/res/values-mn/strings.xml new file mode 100644 index 0000000000..4596577c5d --- /dev/null +++ b/Tethering/res/values-mn/strings.xml @@ -0,0 +1,8 @@ + + + "Модем болгох эсвэл идэвхтэй цэг болгох" + "Тохируулахын тулд товшино уу." + "Модем болгох боломжгүй байна" + "Дэлгэрэнгүй мэдээлэл авахын тулд админтайгаа холбогдоно уу" + diff --git a/Tethering/res/values-mr/strings.xml b/Tethering/res/values-mr/strings.xml new file mode 100644 index 0000000000..85c9ade4fe --- /dev/null +++ b/Tethering/res/values-mr/strings.xml @@ -0,0 +1,8 @@ + + + "टेदरिंग किंवा हॉटस्पॉट सक्रिय" + "सेट करण्यासाठी टॅप करा." + "टेदरिंग बंद आहे" + "तपशीलांसाठी तुमच्या प्रशासकाशी संपर्क साधा" + diff --git a/Tethering/res/values-ms/strings.xml b/Tethering/res/values-ms/strings.xml new file mode 100644 index 0000000000..ec6bdbda08 --- /dev/null +++ b/Tethering/res/values-ms/strings.xml @@ -0,0 +1,8 @@ + + + "Penambatan atau titik panas aktif" + "Ketik untuk membuat persediaan." + "Penambatan dilumpuhkan" + "Hubungi pentadbir anda untuk maklumat lanjut" + diff --git a/Tethering/res/values-my/strings.xml b/Tethering/res/values-my/strings.xml new file mode 100644 index 0000000000..83978b67d4 --- /dev/null +++ b/Tethering/res/values-my/strings.xml @@ -0,0 +1,8 @@ + + + "တဆင့်ပြန်လည်လွှင့်ခြင်း သို့မဟုတ် ဟော့စပေါ့ ဖွင့်ထားသည်" + "စနစ်ထည့်သွင်းရန် တို့ပါ။" + "မိုဘိုင်းဖုန်းကို မိုဒမ်အဖြစ်သုံးခြင်းအား ပိတ်ထားသည်" + "အသေးစိတ်အချက်အလက်များအတွက် သင့်စီမံခန့်ခွဲသူကို ဆက်သွယ်ပါ" + diff --git a/Tethering/res/values-nb/strings.xml b/Tethering/res/values-nb/strings.xml new file mode 100644 index 0000000000..9abf32dd7b --- /dev/null +++ b/Tethering/res/values-nb/strings.xml @@ -0,0 +1,8 @@ + + + "Internettdeling eller trådløs sone er aktiv" + "Trykk for å konfigurere." + "Internettdeling er slått av" + "Ta kontakt med administratoren din for å få mer informasjon" + diff --git a/Tethering/res/values-ne/strings.xml b/Tethering/res/values-ne/strings.xml new file mode 100644 index 0000000000..c8869298a5 --- /dev/null +++ b/Tethering/res/values-ne/strings.xml @@ -0,0 +1,8 @@ + + + "टेथर गर्ने वा हटस्पट सक्रिय" + "सेटअप गर्न ट्याप गर्नुहोस्।" + "टेदरिङलाई असक्षम पारिएको छ" + "विवरणहरूका लागि आफ्ना प्रशासकलाई सम्पर्क गर्नुहोस्" + diff --git a/Tethering/res/values-nl/strings.xml b/Tethering/res/values-nl/strings.xml new file mode 100644 index 0000000000..0ec4bff621 --- /dev/null +++ b/Tethering/res/values-nl/strings.xml @@ -0,0 +1,8 @@ + + + "Tethering of hotspot actief" + "Tik om in te stellen." + "Tethering is uitgeschakeld" + "Neem contact op met je beheerder voor meer informatie" + diff --git a/Tethering/res/values-or/strings.xml b/Tethering/res/values-or/strings.xml new file mode 100644 index 0000000000..457685795a --- /dev/null +++ b/Tethering/res/values-or/strings.xml @@ -0,0 +1,8 @@ + + + "ଟିଥରିଙ୍ଗ କିମ୍ୱା ହଟସ୍ପଟ୍‌ ସକ୍ରିୟ ଅଛି" + "ସେଟଅପ୍‍ କରିବାକୁ ଟାପ୍‍ କରନ୍ତୁ।" + "ଟିଥରିଙ୍ଗ ଅକ୍ଷମ କରାଯାଇଛି" + "ବିବରଣୀ ପାଇଁ ନିଜ ଆଡମିନ୍‌ଙ୍କ ସହ ଯୋଗାଯୋଗ କରନ୍ତୁ" + diff --git a/Tethering/res/values-pa/strings.xml b/Tethering/res/values-pa/strings.xml new file mode 100644 index 0000000000..deddf2ea27 --- /dev/null +++ b/Tethering/res/values-pa/strings.xml @@ -0,0 +1,8 @@ + + + "ਟੈਦਰਿੰਗ ਜਾਂ ਹੌਟਸਪੌਟ ਕਿਰਿਆਸ਼ੀਲ" + "ਸਥਾਪਤ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।" + "ਟੈਦਰਿੰਗ ਨੂੰ ਅਯੋਗ ਬਣਾਇਆ ਗਿਆ ਹੈ" + "ਵੇਰਵਿਆਂ ਲਈ ਆਪਣੇ ਪ੍ਰਸ਼ਾਸਕ ਨੂੰ ਸੰਪਰਕ ਕਰੋ" + diff --git a/Tethering/res/values-pl/strings.xml b/Tethering/res/values-pl/strings.xml new file mode 100644 index 0000000000..48d8468935 --- /dev/null +++ b/Tethering/res/values-pl/strings.xml @@ -0,0 +1,8 @@ + + + "Aktywny tethering lub punkt dostępu" + "Kliknij, by skonfigurować." + "Tethering został wyłączony" + "Aby uzyskać szczegółowe informacje, skontaktuj się z administratorem" + diff --git a/Tethering/res/values-pt-rBR/strings.xml b/Tethering/res/values-pt-rBR/strings.xml new file mode 100644 index 0000000000..32c22b8713 --- /dev/null +++ b/Tethering/res/values-pt-rBR/strings.xml @@ -0,0 +1,8 @@ + + + "Ponto de acesso ou tethering ativo" + "Toque para configurar." + "Tethering desativado" + "Fale com seu administrador para saber detalhes" + diff --git a/Tethering/res/values-pt-rPT/strings.xml b/Tethering/res/values-pt-rPT/strings.xml new file mode 100644 index 0000000000..641e22f44f --- /dev/null +++ b/Tethering/res/values-pt-rPT/strings.xml @@ -0,0 +1,8 @@ + + + "Ligação ponto a ponto ou hotspot activos" + "Toque para configurar." + "A ligação (à Internet) via telemóvel está desativada." + "Contacte o gestor para obter detalhes." + diff --git a/Tethering/res/values-pt/strings.xml b/Tethering/res/values-pt/strings.xml new file mode 100644 index 0000000000..32c22b8713 --- /dev/null +++ b/Tethering/res/values-pt/strings.xml @@ -0,0 +1,8 @@ + + + "Ponto de acesso ou tethering ativo" + "Toque para configurar." + "Tethering desativado" + "Fale com seu administrador para saber detalhes" + diff --git a/Tethering/res/values-ro/strings.xml b/Tethering/res/values-ro/strings.xml new file mode 100644 index 0000000000..f861f733b4 --- /dev/null +++ b/Tethering/res/values-ro/strings.xml @@ -0,0 +1,8 @@ + + + "Tethering sau hotspot activ" + "Atingeți ca să configurați." + "Tetheringul este dezactivat" + "Contactați administratorul pentru detalii" + diff --git a/Tethering/res/values-ru/strings.xml b/Tethering/res/values-ru/strings.xml new file mode 100644 index 0000000000..027cb410c5 --- /dev/null +++ b/Tethering/res/values-ru/strings.xml @@ -0,0 +1,8 @@ + + + "Включен режим модема" + "Нажмите, чтобы настроить." + "Включить режим модема нельзя" + "Обратитесь к администратору, чтобы узнать подробности." + diff --git a/Tethering/res/values-si/strings.xml b/Tethering/res/values-si/strings.xml new file mode 100644 index 0000000000..7d8599f2c2 --- /dev/null +++ b/Tethering/res/values-si/strings.xml @@ -0,0 +1,8 @@ + + + "ටෙදරින් හෝ හොට්ස්පොට් සක්‍රීයයි" + "පිහිටුවීමට තට්ටු කරන්න." + "ටෙදරින් අබල කර ඇත" + "විස්තර සඳහා ඔබගේ පරිපාලක අමතන්න" + diff --git a/Tethering/res/values-sk/strings.xml b/Tethering/res/values-sk/strings.xml new file mode 100644 index 0000000000..a8fe297c00 --- /dev/null +++ b/Tethering/res/values-sk/strings.xml @@ -0,0 +1,8 @@ + + + "Tethering alebo prístupový bod je aktívny" + "Klepnutím prejdete na nastavenie." + "Tethering je deaktivovaný" + "O podrobnosti požiadajte svojho správcu" + diff --git a/Tethering/res/values-sl/strings.xml b/Tethering/res/values-sl/strings.xml new file mode 100644 index 0000000000..b5e5e3856f --- /dev/null +++ b/Tethering/res/values-sl/strings.xml @@ -0,0 +1,8 @@ + + + "Aktivna povezava z internetom ali dostopna točka sta aktivni" + "Dotaknite se, če želite nastaviti." + "Povezava z internetom prek mobilnega telefona je onemogočena" + "Za podrobnosti se obrnite na skrbnika" + diff --git a/Tethering/res/values-sq/strings.xml b/Tethering/res/values-sq/strings.xml new file mode 100644 index 0000000000..fdd4906cc5 --- /dev/null +++ b/Tethering/res/values-sq/strings.xml @@ -0,0 +1,8 @@ + + + "Lidhja e çiftimit ose ajo e qasjes në zona publike interneti është aktive" + "Trokit për ta konfiguruar." + "Lidhja e çiftimit është çaktivizuar" + "Kontakto me administratorin për detaje" + diff --git a/Tethering/res/values-sr/strings.xml b/Tethering/res/values-sr/strings.xml new file mode 100644 index 0000000000..9fab345897 --- /dev/null +++ b/Tethering/res/values-sr/strings.xml @@ -0,0 +1,8 @@ + + + "Активно повезивање са интернетом преко мобилног уређаја или хотспот" + "Додирните да бисте подесили." + "Привезивање је онемогућено" + "Потражите детаље од администратора" + diff --git a/Tethering/res/values-sv/strings.xml b/Tethering/res/values-sv/strings.xml new file mode 100644 index 0000000000..10eeb0fe12 --- /dev/null +++ b/Tethering/res/values-sv/strings.xml @@ -0,0 +1,8 @@ + + + "Internetdelning eller surfzon aktiverad" + "Tryck om du vill konfigurera." + "Internetdelning har inaktiverats" + "Kontakta administratören om du vill veta mer" + diff --git a/Tethering/res/values-sw/strings.xml b/Tethering/res/values-sw/strings.xml new file mode 100644 index 0000000000..3353963077 --- /dev/null +++ b/Tethering/res/values-sw/strings.xml @@ -0,0 +1,8 @@ + + + "Kushiriki au kusambaza intaneti kumewashwa" + "Gusa ili uweke mipangilio." + "Umezima kipengele cha kusambaza mtandao" + "Wasiliana na msimamizi wako ili upate maelezo zaidi" + diff --git a/Tethering/res/values-ta/strings.xml b/Tethering/res/values-ta/strings.xml new file mode 100644 index 0000000000..b1e5cc2413 --- /dev/null +++ b/Tethering/res/values-ta/strings.xml @@ -0,0 +1,8 @@ + + + "டெதெரிங்/ஹாட்ஸ்பாட் இயங்குகிறது" + "அமைக்க, தட்டவும்." + "இணைப்பு முறை முடக்கப்பட்டுள்ளது" + "விவரங்களுக்கு, உங்கள் நிர்வாகியைத் தொடர்புகொள்ளவும்" + diff --git a/Tethering/res/values-te/strings.xml b/Tethering/res/values-te/strings.xml new file mode 100644 index 0000000000..aae40dee40 --- /dev/null +++ b/Tethering/res/values-te/strings.xml @@ -0,0 +1,8 @@ + + + "టీథర్ చేయబడినది లేదా హాట్‌స్పాట్ సక్రియంగా ఉండేది" + "సెటప్ చేయడానికి నొక్కండి." + "టెథెరింగ్ నిలిపివేయబడింది" + "వివరాల కోసం మీ నిర్వాహకులను సంప్రదించండి" + diff --git a/Tethering/res/values-th/strings.xml b/Tethering/res/values-th/strings.xml new file mode 100644 index 0000000000..1b800565ad --- /dev/null +++ b/Tethering/res/values-th/strings.xml @@ -0,0 +1,8 @@ + + + "การปล่อยสัญญาณหรือฮอตสปอตทำงานอยู่" + "แตะเพื่อตั้งค่า" + "ปิดใช้การเชื่อมต่ออินเทอร์เน็ตผ่านมือถือแล้ว" + "ติดต่อผู้ดูแลระบบเพื่อขอรายละเอียด" + diff --git a/Tethering/res/values-tl/strings.xml b/Tethering/res/values-tl/strings.xml new file mode 100644 index 0000000000..12863f90e1 --- /dev/null +++ b/Tethering/res/values-tl/strings.xml @@ -0,0 +1,8 @@ + + + "Pagsasama o aktibong hotspot" + "I-tap upang i-set up." + "Naka-disable ang pag-tether" + "Makipag-ugnayan sa iyong admin para sa mga detalye" + diff --git a/Tethering/res/values-tr/strings.xml b/Tethering/res/values-tr/strings.xml new file mode 100644 index 0000000000..bfcf1ace2c --- /dev/null +++ b/Tethering/res/values-tr/strings.xml @@ -0,0 +1,8 @@ + + + "Tethering veya hotspot etkin" + "Ayarlamak için dokunun." + "Tethering devre dışı bırakıldı" + "Ayrıntılı bilgi için yöneticinize başvurun" + diff --git a/Tethering/res/values-uk/strings.xml b/Tethering/res/values-uk/strings.xml new file mode 100644 index 0000000000..8e159c0723 --- /dev/null +++ b/Tethering/res/values-uk/strings.xml @@ -0,0 +1,8 @@ + + + "Прив\'язка чи точка дост. активна" + "Торкніться, щоб налаштувати." + "Використання телефона в режимі модема вимкнено" + "Щоб дізнатися більше, зв’яжіться з адміністратором" + diff --git a/Tethering/res/values-ur/strings.xml b/Tethering/res/values-ur/strings.xml new file mode 100644 index 0000000000..89195d4aae --- /dev/null +++ b/Tethering/res/values-ur/strings.xml @@ -0,0 +1,8 @@ + + + "ٹیدرنگ یا ہاٹ اسپاٹ فعال" + "سیٹ اپ کرنے کیلئے تھپتھپائیں۔" + "ٹیدرنگ غیر فعال ہے" + "تفصیلات کے لئے اپنے منتظم سے رابطہ کریں" + diff --git a/Tethering/res/values-uz/strings.xml b/Tethering/res/values-uz/strings.xml new file mode 100644 index 0000000000..0ac4d4a741 --- /dev/null +++ b/Tethering/res/values-uz/strings.xml @@ -0,0 +1,8 @@ + + + "Modem rejimi yoniq" + "Sozlash uchun bosing." + "Modem rejimi faolsizlantirildi" + "Tafsilotlari uchun administratoringizga murojaat qiling" + diff --git a/Tethering/res/values-vi/strings.xml b/Tethering/res/values-vi/strings.xml new file mode 100644 index 0000000000..85a4db8aa5 --- /dev/null +++ b/Tethering/res/values-vi/strings.xml @@ -0,0 +1,8 @@ + + + "Chức năng điểm truy cập Internet hoặc điểm phát sóng đang hoạt động" + "Nhấn để thiết lập." + "Đã tắt tính năng chia sẻ kết nối" + "Hãy liên hệ với quản trị viên của bạn để biết chi tiết" + diff --git a/Tethering/res/values-zh-rCN/strings.xml b/Tethering/res/values-zh-rCN/strings.xml new file mode 100644 index 0000000000..ff1fe03953 --- /dev/null +++ b/Tethering/res/values-zh-rCN/strings.xml @@ -0,0 +1,8 @@ + + + "网络共享或热点已启用" + "点按即可进行设置。" + "网络共享已停用" + "请与您的管理员联系以了解详情" + diff --git a/Tethering/res/values-zh-rHK/strings.xml b/Tethering/res/values-zh-rHK/strings.xml new file mode 100644 index 0000000000..0de39fac97 --- /dev/null +++ b/Tethering/res/values-zh-rHK/strings.xml @@ -0,0 +1,8 @@ + + + "已啟用網絡共享或熱點" + "輕按即可設定。" + "網絡共享已停用" + "請聯絡您的管理員以瞭解詳情" + diff --git a/Tethering/res/values-zh-rTW/strings.xml b/Tethering/res/values-zh-rTW/strings.xml new file mode 100644 index 0000000000..9a117bbca4 --- /dev/null +++ b/Tethering/res/values-zh-rTW/strings.xml @@ -0,0 +1,8 @@ + + + "網路共用或無線基地台已啟用" + "輕觸即可進行設定。" + "數據連線已停用" + "詳情請洽你的管理員" + diff --git a/Tethering/res/values-zu/strings.xml b/Tethering/res/values-zu/strings.xml new file mode 100644 index 0000000000..8fe10d86cb --- /dev/null +++ b/Tethering/res/values-zu/strings.xml @@ -0,0 +1,8 @@ + + + "Ukusebenzisa njengemodemu noma i-hotspot ephathekayo kuvuliwe" + "Thepha ukuze usethe." + "Ukusebenzisa ifoni njengemodemu kukhutshaziwe" + "Xhumana nomphathi wakho ukuze uthole imininingwane" + diff --git a/Tethering/res/values/strings.xml b/Tethering/res/values/strings.xml new file mode 100644 index 0000000000..ca866a946e --- /dev/null +++ b/Tethering/res/values/strings.xml @@ -0,0 +1,16 @@ + + + + + Tethering or hotspot active + + Tap to set up. + + + + Tethering is disabled + + Contact your admin for details + \ No newline at end of file diff --git a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java index f1228129cd..7c78ef8994 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java +++ b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java @@ -109,6 +109,7 @@ import com.android.internal.util.MessageUtils; import com.android.internal.util.Protocol; import com.android.internal.util.State; import com.android.internal.util.StateMachine; +import com.android.tethering.R; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -675,14 +676,14 @@ public class Tethering { int icon = 0; switch(id) { case SystemMessage.NOTE_TETHER_USB: - icon = com.android.internal.R.drawable.stat_sys_tether_usb; + icon = R.drawable.stat_sys_tether_usb; break; case SystemMessage.NOTE_TETHER_BLUETOOTH: - icon = com.android.internal.R.drawable.stat_sys_tether_bluetooth; + icon = R.drawable.stat_sys_tether_bluetooth; break; case SystemMessage.NOTE_TETHER_GENERAL: default: - icon = com.android.internal.R.drawable.stat_sys_tether_general; + icon = R.drawable.stat_sys_tether_general; break; } @@ -702,16 +703,16 @@ public class Tethering { PendingIntent pi = PendingIntent.getActivityAsUser(mContext, 0, intent, 0, null, UserHandle.CURRENT); - Resources r = Resources.getSystem(); + Resources r = mContext.getResources(); final CharSequence title; final CharSequence message; if (tetheringOn) { - title = r.getText(com.android.internal.R.string.tethered_notification_title); - message = r.getText(com.android.internal.R.string.tethered_notification_message); + title = r.getText(R.string.tethered_notification_title); + message = r.getText(R.string.tethered_notification_message); } else { - title = r.getText(com.android.internal.R.string.disable_tether_notification_title); - message = r.getText(com.android.internal.R.string.disable_tether_notification_message); + title = r.getText(R.string.disable_tether_notification_title); + message = r.getText(R.string.disable_tether_notification_message); } if (mTetheredNotificationBuilder == null) { @@ -909,7 +910,7 @@ public class Tethering { if (newlyDisallowed && isTetheringActiveOnDevice) { mWrapper.showTetheredNotification( - com.android.internal.R.drawable.stat_sys_tether_general, false); + R.drawable.stat_sys_tether_general, false); mWrapper.untetherAll(); } } From 281d40c8d8de63e5221f3519f05a62b89e3f674b Mon Sep 17 00:00:00 2001 From: Baligh Uddin Date: Sun, 8 Dec 2019 09:55:13 -0800 Subject: [PATCH 007/188] Fix tethering apex version to be 30xxxx series, not 29xxxx BUG: 143747402 Change-Id: Ic1c10100837879a3b4337c7df6cfb36aea8c33c9 --- Tethering/apex/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tethering/apex/manifest.json b/Tethering/apex/manifest.json index 3fb62f3405..078302ac51 100644 --- a/Tethering/apex/manifest.json +++ b/Tethering/apex/manifest.json @@ -1,4 +1,4 @@ { "name": "com.android.tethering.apex", - "version": 290000000 + "version": 300000000 } From d24aeefdbcd2723b1476786cf4f5a05bbbb438bd Mon Sep 17 00:00:00 2001 From: Artur Satayev Date: Wed, 11 Dec 2019 16:33:22 +0000 Subject: [PATCH 008/188] Add new UnsupportedAppUsage annotation as lib dependency. Bug: 145132366 Test: m Change-Id: Ibb7f8424186149365354a96249c06c9fccde66bd --- Tethering/Android.bp | 1 + 1 file changed, 1 insertion(+) diff --git a/Tethering/Android.bp b/Tethering/Android.bp index 7e8721d3cc..3c953b348e 100644 --- a/Tethering/Android.bp +++ b/Tethering/Android.bp @@ -31,6 +31,7 @@ java_defaults { "android.hardware.tetheroffload.control-V1.0-java", "tethering-client", ], + libs: ["unsupportedappusage"], manifest: "AndroidManifestBase.xml", } From 44d5357a6727a4e6ba26c2e04102290454c506fd Mon Sep 17 00:00:00 2001 From: lesl Date: Fri, 13 Dec 2019 10:56:35 +0800 Subject: [PATCH 009/188] tether: Use new API: startTetheredHotspot to enable hotspot Bug: 146180860 Test: atest TetheringTests Change-Id: I14be9c446319fad0706bf8c20faaba7801d638a2 --- .../server/connectivity/tethering/Tethering.java | 2 +- .../connectivity/tethering/TetheringTest.java | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java index 7c78ef8994..743fd6a632 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java +++ b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java @@ -456,7 +456,7 @@ public class Tethering { mLog.e("setWifiTethering: failed to get WifiManager!"); return TETHER_ERROR_SERVICE_UNAVAIL; } - if ((enable && mgr.startSoftAp(null /* use existing wifi config */)) + if ((enable && mgr.startTetheredHotspot(null /* use existing softap config */)) || (!enable && mgr.stopSoftAp())) { mWifiTetherRequested = enable; return TETHER_ERROR_NO_ERROR; diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java index 0273ed3586..cdbc54106c 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java @@ -94,7 +94,7 @@ import android.net.ip.RouterAdvertisementDaemon; import android.net.util.InterfaceParams; import android.net.util.NetworkConstants; import android.net.util.SharedLog; -import android.net.wifi.WifiConfiguration; +import android.net.wifi.SoftApConfiguration; import android.net.wifi.WifiManager; import android.net.wifi.p2p.WifiP2pGroup; import android.net.wifi.p2p.WifiP2pInfo; @@ -806,12 +806,12 @@ public class TetheringTest { // TODO: Test with and without interfaceStatusChanged(). @Test public void failingWifiTetheringLegacyApBroadcast() throws Exception { - when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true); + when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true); // Emulate pressing the WiFi tethering button. mTethering.startTethering(TETHERING_WIFI, null, false); mLooper.dispatchAll(); - verify(mWifiManager, times(1)).startSoftAp(null); + verify(mWifiManager, times(1)).startTetheredHotspot(null); verifyNoMoreInteractions(mWifiManager); verifyNoMoreInteractions(mNMService); @@ -833,12 +833,12 @@ public class TetheringTest { // TODO: Test with and without interfaceStatusChanged(). @Test public void workingWifiTetheringEnrichedApBroadcast() throws Exception { - when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true); + when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true); // Emulate pressing the WiFi tethering button. mTethering.startTethering(TETHERING_WIFI, null, false); mLooper.dispatchAll(); - verify(mWifiManager, times(1)).startSoftAp(null); + verify(mWifiManager, times(1)).startTetheredHotspot(null); verifyNoMoreInteractions(mWifiManager); verifyNoMoreInteractions(mNMService); @@ -907,13 +907,13 @@ public class TetheringTest { // TODO: Test with and without interfaceStatusChanged(). @Test public void failureEnablingIpForwarding() throws Exception { - when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true); + when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true); doThrow(new RemoteException()).when(mNMService).setIpForwardingEnabled(true); // Emulate pressing the WiFi tethering button. mTethering.startTethering(TETHERING_WIFI, null, false); mLooper.dispatchAll(); - verify(mWifiManager, times(1)).startSoftAp(null); + verify(mWifiManager, times(1)).startTetheredHotspot(null); verifyNoMoreInteractions(mWifiManager); verifyNoMoreInteractions(mNMService); @@ -1155,7 +1155,7 @@ public class TetheringTest { when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState); when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())) .thenReturn(upstreamState); - when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true); + when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true); mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true); mLooper.dispatchAll(); tetherState = callback.pollTetherStatesChanged(); From eda5d30035fb24e5260b189bd51115acf8e58b8b Mon Sep 17 00:00:00 2001 From: markchien Date: Wed, 18 Dec 2019 17:47:26 +0800 Subject: [PATCH 010/188] Change Tethering package name ".apex" is easy to confuse. Rename as below: apex: com.android.tethering.apex -> com.android.tethering apk: com.android.tethering -> com.android.networkstack.tethering Bug: 146471733 Test: build Change-Id: I2c7647abb996539a3af6cfe0e0214a5e8927c0d6 --- Tethering/AndroidManifest.xml | 2 +- Tethering/AndroidManifestBase.xml | 2 +- Tethering/AndroidManifest_InProcess.xml | 2 +- Tethering/apex/Android.bp | 14 ++--- Tethering/apex/AndroidManifest.xml | 2 +- .../apex/com.android.tethering.apex.avbpubkey | Bin 1032 -> 0 bytes Tethering/apex/com.android.tethering.apex.pem | 51 ------------------ Tethering/apex/com.android.tethering.apex.pk8 | Bin 2375 -> 0 bytes .../apex/com.android.tethering.apex.x509.pem | 36 ------------- .../apex/com.android.tethering.avbpubkey | Bin 0 -> 1032 bytes Tethering/apex/com.android.tethering.pem | 51 ++++++++++++++++++ Tethering/apex/com.android.tethering.pk8 | Bin 0 -> 2375 bytes Tethering/apex/com.android.tethering.x509.pem | 35 ++++++++++++ Tethering/apex/manifest.json | 2 +- .../connectivity/tethering/Tethering.java | 2 +- Tethering/tests/unit/AndroidManifest.xml | 4 +- 16 files changed, 101 insertions(+), 102 deletions(-) delete mode 100644 Tethering/apex/com.android.tethering.apex.avbpubkey delete mode 100644 Tethering/apex/com.android.tethering.apex.pem delete mode 100644 Tethering/apex/com.android.tethering.apex.pk8 delete mode 100644 Tethering/apex/com.android.tethering.apex.x509.pem create mode 100644 Tethering/apex/com.android.tethering.avbpubkey create mode 100644 Tethering/apex/com.android.tethering.pem create mode 100644 Tethering/apex/com.android.tethering.pk8 create mode 100644 Tethering/apex/com.android.tethering.x509.pem diff --git a/Tethering/AndroidManifest.xml b/Tethering/AndroidManifest.xml index 1430ed00aa..8ba05df99e 100644 --- a/Tethering/AndroidManifest.xml +++ b/Tethering/AndroidManifest.xml @@ -17,7 +17,7 @@ */ --> diff --git a/Tethering/AndroidManifestBase.xml b/Tethering/AndroidManifestBase.xml index dc013da338..fa85f66489 100644 --- a/Tethering/AndroidManifestBase.xml +++ b/Tethering/AndroidManifestBase.xml @@ -17,7 +17,7 @@ */ --> diff --git a/Tethering/apex/Android.bp b/Tethering/apex/Android.bp index bca01ebdf8..af6af93650 100644 --- a/Tethering/apex/Android.bp +++ b/Tethering/apex/Android.bp @@ -15,21 +15,21 @@ // apex { - name: "com.android.tethering.apex", + name: "com.android.tethering", apps: ["Tethering"], manifest: "manifest.json", - key: "com.android.tethering.apex.key", + key: "com.android.tethering.key", androidManifest: "AndroidManifest.xml", } apex_key { - name: "com.android.tethering.apex.key", - public_key: "com.android.tethering.apex.avbpubkey", - private_key: "com.android.tethering.apex.pem", + name: "com.android.tethering.key", + public_key: "com.android.tethering.avbpubkey", + private_key: "com.android.tethering.pem", } android_app_certificate { - name: "com.android.tethering.apex.certificate", - certificate: "com.android.tethering.apex", + name: "com.android.tethering.certificate", + certificate: "com.android.tethering", } diff --git a/Tethering/apex/AndroidManifest.xml b/Tethering/apex/AndroidManifest.xml index 7769b799b6..5c35c51dc7 100644 --- a/Tethering/apex/AndroidManifest.xml +++ b/Tethering/apex/AndroidManifest.xml @@ -15,7 +15,7 @@ * limitations under the License. --> + package="com.android.tethering"> + package="com.android.networkstack.tethering.tests.unit"> From e7ccfca9da333e755d1b1fa9065543db788323b6 Mon Sep 17 00:00:00 2001 From: markchien Date: Mon, 16 Dec 2019 20:15:20 +0800 Subject: [PATCH 011/188] [Tether13] Move TetheringManager into framework Move tethering out of ConnectivityService. All client would use TetheringManager to talk with TetheringService directly. Bug: 144320246 Test: -build, flash, boot -atest TetheringTests Change-Id: Ib051bea724a256f9c4572b566e46ae7b9c4abe6e --- Tethering/Android.bp | 11 +- Tethering/AndroidManifest.xml | 15 + Tethering/AndroidManifest_InProcess.xml | 4 +- Tethering/CleanSpec.mk | 6 + Tethering/apex/Android.bp | 1 + Tethering/common/TetheringLib/Android.bp | 32 +- .../common/TetheringLib/jarjar-rules.txt | 1 + .../src/android/net/IIntResultListener.aidl | 25 + .../src/android/net/ITetheringConnector.aidl | 24 +- ...back.aidl => ITetheringEventCallback.aidl} | 9 +- .../src/android/net/TetheringManager.java | 625 +++++++++--------- Tethering/jarjar-rules.txt | 15 + Tethering/proguard.flags | 10 +- Tethering/src/android/net/ip/IpServer.java | 17 +- .../connectivity/tethering/Tethering.java | 86 ++- .../tethering/TetheringDependencies.java | 18 +- .../tethering/TetheringService.java | 253 ++++++- Tethering/tests/unit/Android.bp | 2 + Tethering/tests/unit/jarjar-rules.txt | 11 + .../connectivity/tethering/TetheringTest.java | 53 +- 20 files changed, 785 insertions(+), 433 deletions(-) create mode 100644 Tethering/common/TetheringLib/jarjar-rules.txt create mode 100644 Tethering/common/TetheringLib/src/android/net/IIntResultListener.aidl rename Tethering/common/TetheringLib/src/android/net/{ITetherInternalCallback.aidl => ITetheringEventCallback.aidl} (83%) create mode 100644 Tethering/jarjar-rules.txt create mode 100644 Tethering/tests/unit/jarjar-rules.txt diff --git a/Tethering/Android.bp b/Tethering/Android.bp index 3c953b348e..08552cbfd3 100644 --- a/Tethering/Android.bp +++ b/Tethering/Android.bp @@ -29,9 +29,11 @@ java_defaults { "netlink-client", "networkstack-aidl-interfaces-unstable-java", "android.hardware.tetheroffload.control-V1.0-java", - "tethering-client", ], - libs: ["unsupportedappusage"], + libs: [ + "framework-tethering", + ], + manifest: "AndroidManifestBase.xml", } @@ -90,6 +92,10 @@ java_defaults { resource_dirs: [ "res", ], + libs: [ + "framework-tethering", + ], + jarjar_rules: "jarjar-rules.txt", optimize: { proguard_flags_files: ["proguard.flags"], }, @@ -104,7 +110,6 @@ android_app { manifest: "AndroidManifest_InProcess.xml", // InProcessTethering is a replacement for Tethering overrides: ["Tethering"], - // TODO: use PlatformNetworkPermissionConfig. } // Updatable tethering packaged as an application diff --git a/Tethering/AndroidManifest.xml b/Tethering/AndroidManifest.xml index 8ba05df99e..87a8c3f5c6 100644 --- a/Tethering/AndroidManifest.xml +++ b/Tethering/AndroidManifest.xml @@ -21,6 +21,21 @@ android:sharedUserId="android.uid.networkstack"> + + + + + + + + + + + + - + android:permission="android.permission.MAINLINE_NETWORK_STACK"> diff --git a/Tethering/CleanSpec.mk b/Tethering/CleanSpec.mk index 70db351a69..30bdd581d0 100644 --- a/Tethering/CleanSpec.mk +++ b/Tethering/CleanSpec.mk @@ -46,6 +46,12 @@ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/Tethering) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/InProcessTethering) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/InProcessTethering*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/InProcessTethering*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system_other/system/priv-app/InProcessTethering) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/apex/com.android.tethering) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/apex/com.android.tethering.apex) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/ETC/com.android.tethering*) # ****************************************************************** # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER diff --git a/Tethering/apex/Android.bp b/Tethering/apex/Android.bp index af6af93650..94ef11cc12 100644 --- a/Tethering/apex/Android.bp +++ b/Tethering/apex/Android.bp @@ -16,6 +16,7 @@ apex { name: "com.android.tethering", + java_libs: ["framework-tethering"], apps: ["Tethering"], manifest: "manifest.json", key: "com.android.tethering.key", diff --git a/Tethering/common/TetheringLib/Android.bp b/Tethering/common/TetheringLib/Android.bp index adc5a723a9..5785707cb9 100644 --- a/Tethering/common/TetheringLib/Android.bp +++ b/Tethering/common/TetheringLib/Android.bp @@ -12,7 +12,6 @@ // 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. -// // AIDL interfaces between the core system and the tethering mainline module. aidl_interface { @@ -20,10 +19,7 @@ aidl_interface { local_include_dir: "src", include_dirs: ["frameworks/base/core/java"], // For framework parcelables. srcs: [ - "src/android/net/ITetherInternalCallback.aidl", - "src/android/net/ITetheringConnector.aidl", - "src/android/net/TetheringConfigurationParcel.aidl", - "src/android/net/TetherStatesParcel.aidl", + "src/android/net/*.aidl", ], backend: { ndk: { @@ -36,16 +32,32 @@ aidl_interface { } java_library { - name: "tethering-client", + name: "framework-tethering", sdk_version: "system_current", + srcs: [ + "src/android/net/TetheringManager.java", + ":framework-tethering-annotations", + ], static_libs: [ "tethering-aidl-interfaces-java", ], + jarjar_rules: "jarjar-rules.txt", + installable: true, + + libs: [ + "android_system_stubs_current", + ], } -// This is temporary file group which would be removed after TetheringManager is built -// into tethering-client. Will be done by aosp/1156906. filegroup { - name: "tethering-manager", - srcs: ["src/android/net/TetheringManager.java"], + name: "framework-tethering-srcs", + srcs: [ + "src/android/net/TetheringManager.java", + "src/android/net/IIntResultListener.aidl", + "src/android/net/ITetheringEventCallback.aidl", + "src/android/net/ITetheringConnector.aidl", + "src/android/net/TetheringConfigurationParcel.aidl", + "src/android/net/TetherStatesParcel.aidl", + ], + path: "src" } diff --git a/Tethering/common/TetheringLib/jarjar-rules.txt b/Tethering/common/TetheringLib/jarjar-rules.txt new file mode 100644 index 0000000000..35e0f88e70 --- /dev/null +++ b/Tethering/common/TetheringLib/jarjar-rules.txt @@ -0,0 +1 @@ +rule android.annotation.** com.android.networkstack.tethering.annotation.@1 diff --git a/Tethering/common/TetheringLib/src/android/net/IIntResultListener.aidl b/Tethering/common/TetheringLib/src/android/net/IIntResultListener.aidl new file mode 100644 index 0000000000..c3d66ee145 --- /dev/null +++ b/Tethering/common/TetheringLib/src/android/net/IIntResultListener.aidl @@ -0,0 +1,25 @@ +/* + * 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; + +/** + * Listener interface allowing objects to listen to various module event. + * {@hide} + */ +oneway interface IIntResultListener { + void onResult(int resultCode); +} diff --git a/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl b/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl index bfe502fbeb..d30c399869 100644 --- a/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl +++ b/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl @@ -15,23 +15,31 @@ */ package android.net; -import android.net.ITetherInternalCallback; +import android.net.IIntResultListener; +import android.net.ITetheringEventCallback; import android.os.ResultReceiver; /** @hide */ oneway interface ITetheringConnector { - void tether(String iface); + void tether(String iface, String callerPkg, IIntResultListener receiver); - void untether(String iface); + void untether(String iface, String callerPkg, IIntResultListener receiver); - void setUsbTethering(boolean enable); + void setUsbTethering(boolean enable, String callerPkg, IIntResultListener receiver); - void startTethering(int type, in ResultReceiver receiver, boolean showProvisioningUi); + void startTethering(int type, in ResultReceiver receiver, boolean showProvisioningUi, + String callerPkg); - void stopTethering(int type); + void stopTethering(int type, String callerPkg, IIntResultListener receiver); void requestLatestTetheringEntitlementResult(int type, in ResultReceiver receiver, - boolean showEntitlementUi); + boolean showEntitlementUi, String callerPkg); - void registerTetherInternalCallback(ITetherInternalCallback callback); + void registerTetheringEventCallback(ITetheringEventCallback callback, String callerPkg); + + void unregisterTetheringEventCallback(ITetheringEventCallback callback, String callerPkg); + + void isTetheringSupported(String callerPkg, IIntResultListener receiver); + + void stopAllTethering(String callerPkg, IIntResultListener receiver); } diff --git a/Tethering/common/TetheringLib/src/android/net/ITetherInternalCallback.aidl b/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl similarity index 83% rename from Tethering/common/TetheringLib/src/android/net/ITetherInternalCallback.aidl rename to Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl index abb00e819f..28361954e1 100644 --- a/Tethering/common/TetheringLib/src/android/net/ITetherInternalCallback.aidl +++ b/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl @@ -21,14 +21,15 @@ import android.net.TetheringConfigurationParcel; import android.net.TetherStatesParcel; /** - * Callback class for receiving tethering changed events + * Callback class for receiving tethering changed events. * @hide */ -oneway interface ITetherInternalCallback +oneway interface ITetheringEventCallback { + void onCallbackStarted(in Network network, in TetheringConfigurationParcel config, + in TetherStatesParcel states); + void onCallbackStopped(int errorCode); void onUpstreamChanged(in Network network); void onConfigurationChanged(in TetheringConfigurationParcel config); void onTetherStatesChanged(in TetherStatesParcel states); - void onCallbackCreated(in Network network, in TetheringConfigurationParcel config, - in TetherStatesParcel states); } diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java index 7fb286b5fa..a49ab85d2f 100644 --- a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java +++ b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java @@ -15,94 +15,141 @@ */ package android.net; -import static android.Manifest.permission.NETWORK_STACK; import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR; -import static android.net.ConnectivityManager.TETHER_ERROR_SERVICE_UNAVAIL; import android.annotation.NonNull; -import android.annotation.Nullable; -import android.net.util.SharedLog; +import android.content.Context; +import android.net.ConnectivityManager.OnTetheringEventCallback; import android.os.ConditionVariable; import android.os.IBinder; -import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ResultReceiver; -import android.util.Slog; +import android.util.ArrayMap; +import android.util.Log; -import com.android.internal.annotations.GuardedBy; - -import java.io.PrintWriter; -import java.util.StringJoiner; +import java.util.concurrent.Executor; /** - * Service used to communicate with the tethering, which is running in a separate module. + * This class provides the APIs to control the tethering service. + *

The primary responsibilities of this class are to provide the APIs for applications to + * start tethering, stop tethering, query configuration and query status. + * * @hide */ +// TODO: make it @SystemApi public class TetheringManager { private static final String TAG = TetheringManager.class.getSimpleName(); + private static final int DEFAULT_TIMEOUT_MS = 60_000; private static TetheringManager sInstance; - @Nullable - private ITetheringConnector mConnector; - private TetherInternalCallback mCallback; - private Network mTetherUpstream; + private final ITetheringConnector mConnector; + private final TetheringCallbackInternal mCallback; + private final Context mContext; + private final ArrayMap + mTetheringEventCallbacks = new ArrayMap<>(); + private TetheringConfigurationParcel mTetheringConfiguration; private TetherStatesParcel mTetherStatesParcel; - private final RemoteCallbackList mTetheringEventCallbacks = - new RemoteCallbackList<>(); - @GuardedBy("mLog") - private final SharedLog mLog = new SharedLog(TAG); - - private TetheringManager() { } + public static final int TETHER_ERROR_NO_ERROR = 0; + public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; + public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; + public static final int TETHER_ERROR_UNSUPPORTED = 3; + public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; + public static final int TETHER_ERROR_MASTER_ERROR = 5; + public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; + public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; + public static final int TETHER_ERROR_ENABLE_NAT_ERROR = 8; + public static final int TETHER_ERROR_DISABLE_NAT_ERROR = 9; + public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; + public static final int TETHER_ERROR_PROVISION_FAILED = 11; + public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; + public static final int TETHER_ERROR_ENTITLEMENT_UNKONWN = 13; + public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; + public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; /** - * Get the TetheringManager singleton instance. + * Create a TetheringManager object for interacting with the tethering service. */ - public static synchronized TetheringManager getInstance() { - if (sInstance == null) { - sInstance = new TetheringManager(); - } - return sInstance; - } + public TetheringManager(@NonNull final Context context, @NonNull final IBinder service) { + mContext = context; + mConnector = ITetheringConnector.Stub.asInterface(service); + mCallback = new TetheringCallbackInternal(); - private class TetheringConnection implements - ConnectivityModuleConnector.ModuleServiceCallback { - @Override - public void onModuleServiceConnected(@NonNull IBinder service) { - logi("Tethering service connected"); - registerTetheringService(service); - } - } - - private void registerTetheringService(@NonNull IBinder service) { - final ITetheringConnector connector = ITetheringConnector.Stub.asInterface(service); - - log("Tethering service registered"); - - // Currently TetheringManager instance is only used by ConnectivityService and mConnector - // only expect to assign once when system server start and bind tethering service. - // STOPSHIP: Change mConnector to final before TetheringManager put into boot classpath. - mConnector = connector; - mCallback = new TetherInternalCallback(); + final String pkgName = mContext.getOpPackageName(); + Log.i(TAG, "registerTetheringEventCallback:" + pkgName); try { - mConnector.registerTetherInternalCallback(mCallback); + mConnector.registerTetheringEventCallback(mCallback, pkgName); } catch (RemoteException e) { - e.rethrowFromSystemServer(); + throw new IllegalStateException(e); } } - private class TetherInternalCallback extends ITetherInternalCallback.Stub { - private final ConditionVariable mWaitForCallback = new ConditionVariable(false); - private static final int EVENT_CALLBACK_TIMEOUT_MS = 60_000; + private interface RequestHelper { + void runRequest(IIntResultListener listener); + } + + private class RequestDispatcher { + private final ConditionVariable mWaiting; + public int mRemoteResult; + + private final IIntResultListener mListener = new IIntResultListener.Stub() { + @Override + public void onResult(final int resultCode) { + mRemoteResult = resultCode; + mWaiting.open(); + } + }; + + RequestDispatcher() { + mWaiting = new ConditionVariable(); + } + + int waitForResult(final RequestHelper request) { + request.runRequest(mListener); + if (!mWaiting.block(DEFAULT_TIMEOUT_MS)) { + throw new IllegalStateException("Callback timeout"); + } + + throwIfPermissionFailure(mRemoteResult); + + return mRemoteResult; + } + } + + private void throwIfPermissionFailure(final int errorCode) { + switch (errorCode) { + case TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION: + throw new SecurityException("No android.permission.TETHER_PRIVILEGED" + + " or android.permission.WRITE_SETTINGS permission"); + case TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION: + throw new SecurityException( + "No android.permission.ACCESS_NETWORK_STATE permission"); + } + } + + private class TetheringCallbackInternal extends ITetheringEventCallback.Stub { + private int mError = TETHER_ERROR_NO_ERROR; + private final ConditionVariable mWaitForCallback = new ConditionVariable(); @Override - public void onUpstreamChanged(Network network) { - mTetherUpstream = network; - reportUpstreamChanged(network); + public void onCallbackStarted(Network network, TetheringConfigurationParcel config, + TetherStatesParcel states) { + mTetheringConfiguration = config; + mTetherStatesParcel = states; + mWaitForCallback.open(); } + @Override + public void onCallbackStopped(int errorCode) { + mError = errorCode; + mWaitForCallback.open(); + } + + @Override + public void onUpstreamChanged(Network network) { } + @Override public void onConfigurationChanged(TetheringConfigurationParcel config) { mTetheringConfiguration = config; @@ -113,49 +160,10 @@ public class TetheringManager { mTetherStatesParcel = states; } - @Override - public void onCallbackCreated(Network network, TetheringConfigurationParcel config, - TetherStatesParcel states) { - mTetherUpstream = network; - mTetheringConfiguration = config; - mTetherStatesParcel = states; - mWaitForCallback.open(); + public void waitForStarted() { + mWaitForCallback.block(DEFAULT_TIMEOUT_MS); + throwIfPermissionFailure(mError); } - - boolean awaitCallbackCreation() { - return mWaitForCallback.block(EVENT_CALLBACK_TIMEOUT_MS); - } - } - - private void reportUpstreamChanged(Network network) { - final int length = mTetheringEventCallbacks.beginBroadcast(); - try { - for (int i = 0; i < length; i++) { - try { - mTetheringEventCallbacks.getBroadcastItem(i).onUpstreamChanged(network); - } catch (RemoteException e) { - // Not really very much to do here. - } - } - } finally { - mTetheringEventCallbacks.finishBroadcast(); - } - } - - /** - * Start the tethering service. Should be called only once on device startup. - * - *

This method will start the tethering service either in the network stack process, - * or inside the system server on devices that do not support the tethering module. - * - * {@hide} - */ - public void start() { - // Using MAINLINE_NETWORK_STACK permission after cutting off the dpendency of system server. - ConnectivityModuleConnector.getInstance().startModuleService( - ITetheringConnector.class.getName(), NETWORK_STACK, - new TetheringConnection()); - log("Tethering service start requested"); } /** @@ -165,108 +173,110 @@ public class TetheringManager { * IP network interface is available, dhcp will still run and traffic will be * allowed between the tethered devices and this device, though upstream net * access will of course fail until an upstream network interface becomes - * active. Note: return value do not have any meaning. It is better to use - * #getTetherableIfaces() to ensure corresponding interface is available for - * tethering before calling #tether(). + * active. * - * @deprecated The only usages should be in PanService and Wifi P2P which - * need direct access. + * @deprecated The only usages is PanService. It uses this for legacy reasons + * and will migrate away as soon as possible. * - * {@hide} + * @param iface the interface name to tether. + * @return error a {@code TETHER_ERROR} value indicating success or failure type */ @Deprecated - public int tether(@NonNull String iface) { - if (mConnector == null) { - Slog.wtf(TAG, "Tethering not ready yet"); - return TETHER_ERROR_SERVICE_UNAVAIL; - } - try { - mConnector.tether(iface); - } catch (RemoteException e) { - e.rethrowFromSystemServer(); - } - return TETHER_ERROR_NO_ERROR; + public int tether(@NonNull final String iface) { + final String callerPkg = mContext.getOpPackageName(); + Log.i(TAG, "tether caller:" + callerPkg); + final RequestDispatcher dispatcher = new RequestDispatcher(); + + return dispatcher.waitForResult(listener -> { + try { + mConnector.tether(iface, callerPkg, listener); + } catch (RemoteException e) { + throw new IllegalStateException(e); + } + }); } /** * Stop tethering the named interface. * - * @deprecated - * {@hide} + * @deprecated The only usages is PanService. It uses this for legacy reasons + * and will migrate away as soon as possible. */ @Deprecated - public int untether(@NonNull String iface) { - if (mConnector == null) { - Slog.wtf(TAG, "Tethering not ready yet"); - return TETHER_ERROR_SERVICE_UNAVAIL; - } - try { - mConnector.untether(iface); - } catch (RemoteException e) { - e.rethrowFromSystemServer(); - } - return TETHER_ERROR_NO_ERROR; + public int untether(@NonNull final String iface) { + final String callerPkg = mContext.getOpPackageName(); + Log.i(TAG, "untether caller:" + callerPkg); + + final RequestDispatcher dispatcher = new RequestDispatcher(); + + return dispatcher.waitForResult(listener -> { + try { + mConnector.untether(iface, callerPkg, listener); + } catch (RemoteException e) { + throw new IllegalStateException(e); + } + }); } /** - * Attempt to both alter the mode of USB and Tethering of USB. WARNING: New client 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. + * Attempt to both alter the mode of USB and Tethering of USB. * - * @deprecated - * {@hide} + * @deprecated New client 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. */ @Deprecated - public int setUsbTethering(boolean enable) { - if (mConnector == null) { - Slog.wtf(TAG, "Tethering not ready yet"); - return TETHER_ERROR_SERVICE_UNAVAIL; - } - try { - mConnector.setUsbTethering(enable); - } catch (RemoteException e) { - e.rethrowFromSystemServer(); - } - return TETHER_ERROR_NO_ERROR; + public int setUsbTethering(final boolean enable) { + final String callerPkg = mContext.getOpPackageName(); + Log.i(TAG, "setUsbTethering caller:" + callerPkg); + + final RequestDispatcher dispatcher = new RequestDispatcher(); + + return dispatcher.waitForResult(listener -> { + try { + mConnector.setUsbTethering(enable, callerPkg, listener); + } catch (RemoteException e) { + throw new IllegalStateException(e); + } + }); } /** * Starts tethering and runs tether provisioning for the given type if needed. If provisioning * fails, stopTethering will be called automatically. * - * {@hide} */ // TODO: improve the usage of ResultReceiver, b/145096122 - public void startTethering(int type, @NonNull ResultReceiver receiver, - boolean showProvisioningUi) { - if (mConnector == null) { - Slog.wtf(TAG, "Tethering not ready yet"); - return; - } + public void startTethering(final int type, @NonNull final ResultReceiver receiver, + final boolean showProvisioningUi) { + final String callerPkg = mContext.getOpPackageName(); + Log.i(TAG, "startTethering caller:" + callerPkg); + try { - mConnector.startTethering(type, receiver, showProvisioningUi); + mConnector.startTethering(type, receiver, showProvisioningUi, callerPkg); } catch (RemoteException e) { - e.rethrowFromSystemServer(); + throw new IllegalStateException(e); } } /** * Stops tethering for the given type. Also cancels any provisioning rechecks for that type if * applicable. - * - * {@hide} */ - public void stopTethering(int type) { - if (mConnector == null) { - Slog.wtf(TAG, "Tethering not ready yet"); - return; - } - try { - mConnector.stopTethering(type); - } catch (RemoteException e) { - e.rethrowFromSystemServer(); - } + public void stopTethering(final int type) { + final String callerPkg = mContext.getOpPackageName(); + Log.i(TAG, "stopTethering caller:" + callerPkg); + + final RequestDispatcher dispatcher = new RequestDispatcher(); + + dispatcher.waitForResult(listener -> { + try { + mConnector.stopTethering(type, callerPkg, listener); + } catch (RemoteException e) { + throw new IllegalStateException(e); + } + }); } /** @@ -277,47 +287,109 @@ public class TetheringManager { * if it's really needed. */ // TODO: improve the usage of ResultReceiver, b/145096122 - public void requestLatestTetheringEntitlementResult(int type, @NonNull ResultReceiver receiver, - boolean showEntitlementUi) { - if (mConnector == null) { - Slog.wtf(TAG, "Tethering not ready yet"); - return; - } + public void requestLatestTetheringEntitlementResult(final int type, + @NonNull final ResultReceiver receiver, final boolean showEntitlementUi) { + final String callerPkg = mContext.getOpPackageName(); + Log.i(TAG, "getLatestTetheringEntitlementResult caller:" + callerPkg); + try { - mConnector.requestLatestTetheringEntitlementResult(type, receiver, showEntitlementUi); + mConnector.requestLatestTetheringEntitlementResult(type, receiver, showEntitlementUi, + callerPkg); } catch (RemoteException e) { - e.rethrowFromSystemServer(); + throw new IllegalStateException(e); } } /** - * Register tethering event callback. + * Start listening to tethering change events. Any new added callback will receive the last + * tethering status right away. If callback is registered, + * {@link OnTetheringEventCallback#onUpstreamChanged} will immediately be called. If tethering + * has no upstream or disabled, the argument of callback will be null. The same callback object + * cannot be registered twice. * - * {@hide} + * @param executor the executor on which callback will be invoked. + * @param callback the callback to be called when tethering has change events. */ - public void registerTetheringEventCallback(@NonNull ITetheringEventCallback callback) { - mTetheringEventCallbacks.register(callback); + public void registerTetheringEventCallback(@NonNull Executor executor, + @NonNull OnTetheringEventCallback callback) { + final String callerPkg = mContext.getOpPackageName(); + Log.i(TAG, "registerTetheringEventCallback caller:" + callerPkg); + + synchronized (mTetheringEventCallbacks) { + if (!mTetheringEventCallbacks.containsKey(callback)) { + throw new IllegalArgumentException("callback was already registered."); + } + final ITetheringEventCallback remoteCallback = new ITetheringEventCallback.Stub() { + @Override + public void onUpstreamChanged(Network network) throws RemoteException { + executor.execute(() -> { + callback.onUpstreamChanged(network); + }); + } + + @Override + public void onCallbackStarted(Network network, TetheringConfigurationParcel config, + TetherStatesParcel states) { + executor.execute(() -> { + callback.onUpstreamChanged(network); + }); + } + + @Override + public void onCallbackStopped(int errorCode) { + executor.execute(() -> { + throwIfPermissionFailure(errorCode); + }); + } + + @Override + public void onConfigurationChanged(TetheringConfigurationParcel config) { } + + @Override + public void onTetherStatesChanged(TetherStatesParcel states) { } + }; + try { + mConnector.registerTetheringEventCallback(remoteCallback, callerPkg); + } catch (RemoteException e) { + throw new IllegalStateException(e); + } + mTetheringEventCallbacks.put(callback, remoteCallback); + } } /** - * Unregister tethering event callback. + * Remove tethering event callback previously registered with + * {@link #registerTetheringEventCallback}. * - * {@hide} + * @param callback previously registered callback. */ - public void unregisterTetheringEventCallback(@NonNull ITetheringEventCallback callback) { - mTetheringEventCallbacks.unregister(callback); + public void unregisterTetheringEventCallback(@NonNull final OnTetheringEventCallback callback) { + final String callerPkg = mContext.getOpPackageName(); + Log.i(TAG, "unregisterTetheringEventCallback caller:" + callerPkg); + + synchronized (mTetheringEventCallbacks) { + ITetheringEventCallback remoteCallback = mTetheringEventCallbacks.remove(callback); + if (remoteCallback == null) { + throw new IllegalArgumentException("callback was not registered."); + } + try { + mConnector.unregisterTetheringEventCallback(remoteCallback, callerPkg); + } catch (RemoteException e) { + throw new IllegalStateException(e); + } + } } /** * Get a more detailed error code after a Tethering or Untethering * request asynchronously failed. * - * {@hide} + * @param iface The name of the interface of interest + * @return error The error code of the last error tethering or untethering the named + * interface */ - public int getLastTetherError(@NonNull String iface) { - if (!mCallback.awaitCallbackCreation()) { - throw new NullPointerException("callback was not ready yet"); - } + public int getLastTetherError(@NonNull final String iface) { + mCallback.waitForStarted(); if (mTetherStatesParcel == null) return TETHER_ERROR_NO_ERROR; int i = 0; @@ -334,12 +406,11 @@ public class TetheringManager { * USB network interfaces. If USB tethering is not supported by the * device, this list should be empty. * - * {@hide} + * @return an array of 0 or more regular expression Strings defining + * what interfaces are considered tetherable usb interfaces. */ public @NonNull String[] getTetherableUsbRegexs() { - if (!mCallback.awaitCallbackCreation()) { - throw new NullPointerException("callback was not ready yet"); - } + mCallback.waitForStarted(); return mTetheringConfiguration.tetherableUsbRegexs; } @@ -348,12 +419,11 @@ public class TetheringManager { * Wifi network interfaces. If Wifi tethering is not supported by the * device, this list should be empty. * - * {@hide} + * @return an array of 0 or more regular expression Strings defining + * what interfaces are considered tetherable wifi interfaces. */ public @NonNull String[] getTetherableWifiRegexs() { - if (!mCallback.awaitCallbackCreation()) { - throw new NullPointerException("callback was not ready yet"); - } + mCallback.waitForStarted(); return mTetheringConfiguration.tetherableWifiRegexs; } @@ -362,12 +432,11 @@ public class TetheringManager { * Bluetooth network interfaces. If Bluetooth tethering is not supported by the * device, this list should be empty. * - * {@hide} + * @return an array of 0 or more regular expression Strings defining + * what interfaces are considered tetherable bluetooth interfaces. */ public @NonNull String[] getTetherableBluetoothRegexs() { - if (!mCallback.awaitCallbackCreation()) { - throw new NullPointerException("callback was not ready yet"); - } + mCallback.waitForStarted(); return mTetheringConfiguration.tetherableBluetoothRegexs; } @@ -375,40 +444,42 @@ public class TetheringManager { * Get the set of tetherable, available interfaces. This list is limited by * device configuration and current interface existence. * - * {@hide} + * @return an array of 0 or more Strings of tetherable interface names. */ public @NonNull String[] getTetherableIfaces() { - if (!mCallback.awaitCallbackCreation()) { - throw new NullPointerException("callback was not ready yet"); - } + mCallback.waitForStarted(); if (mTetherStatesParcel == null) return new String[0]; + return mTetherStatesParcel.availableList; } /** * Get the set of tethered interfaces. * - * {@hide} + * @return an array of 0 or more String of currently tethered interface names. */ public @NonNull String[] getTetheredIfaces() { - if (!mCallback.awaitCallbackCreation()) { - throw new NullPointerException("callback was not ready yet"); - } + mCallback.waitForStarted(); if (mTetherStatesParcel == null) return new String[0]; + return mTetherStatesParcel.tetheredList; } /** * Get the set of interface names which attempted to tether but - * failed. + * failed. Re-attempting to tether may cause them to reset to the Tethered + * state. Alternatively, causing the interface to be destroyed and recreated + * may cause them to reset to the available state. + * {@link ConnectivityManager#getLastTetherError} can be used to get more + * information on the cause of the errors. * - * {@hide} + * @return an array of 0 or more String indicating the interface names + * which failed to tether. */ public @NonNull String[] getTetheringErroredIfaces() { - if (!mCallback.awaitCallbackCreation()) { - throw new NullPointerException("callback was not ready yet"); - } + mCallback.waitForStarted(); if (mTetherStatesParcel == null) return new String[0]; + return mTetherStatesParcel.erroredIfaceList; } @@ -416,123 +487,49 @@ public class TetheringManager { * Get the set of tethered dhcp ranges. * * @deprecated This API just return the default value which is not used in DhcpServer. - * {@hide} */ @Deprecated public @NonNull String[] getTetheredDhcpRanges() { - if (!mCallback.awaitCallbackCreation()) { - throw new NullPointerException("callback was not ready yet"); - } + mCallback.waitForStarted(); return mTetheringConfiguration.legacyDhcpRanges; } /** - * Check if the device allows for tethering. + * Check if the device allows for tethering. It may be disabled via + * {@code ro.tether.denied} system property, Settings.TETHER_SUPPORTED or + * due to device configuration. * - * {@hide} + * @return a boolean - {@code true} indicating Tethering is supported. */ - public boolean hasTetherableConfiguration() { - if (!mCallback.awaitCallbackCreation()) { - throw new NullPointerException("callback was not ready yet"); - } - final boolean hasDownstreamConfiguration = - (mTetheringConfiguration.tetherableUsbRegexs.length != 0) - || (mTetheringConfiguration.tetherableWifiRegexs.length != 0) - || (mTetheringConfiguration.tetherableBluetoothRegexs.length != 0); - final boolean hasUpstreamConfiguration = - (mTetheringConfiguration.preferredUpstreamIfaceTypes.length != 0) - || mTetheringConfiguration.chooseUpstreamAutomatically; + public boolean isTetheringSupported() { + final String callerPkg = mContext.getOpPackageName(); - return hasDownstreamConfiguration && hasUpstreamConfiguration; + final RequestDispatcher dispatcher = new RequestDispatcher(); + final int ret = dispatcher.waitForResult(listener -> { + try { + mConnector.isTetheringSupported(callerPkg, listener); + } catch (RemoteException e) { + throw new IllegalStateException(e); + } + }); + + return ret == TETHER_ERROR_NO_ERROR; } /** - * Log a message in the local log. + * Stop all active tethering. */ - private void log(@NonNull String message) { - synchronized (mLog) { - mLog.log(message); - } - } + public void stopAllTethering() { + final String callerPkg = mContext.getOpPackageName(); + Log.i(TAG, "stopAllTethering caller:" + callerPkg); - /** - * Log a condition that should never happen. - */ - private void logWtf(@NonNull String message, @Nullable Throwable e) { - Slog.wtf(TAG, message); - synchronized (mLog) { - mLog.e(message, e); - } - } - - /** - * Log a ERROR level message in the local and system logs. - */ - private void loge(@NonNull String message, @Nullable Throwable e) { - synchronized (mLog) { - mLog.e(message, e); - } - } - - /** - * Log a INFO level message in the local and system logs. - */ - private void logi(@NonNull String message) { - synchronized (mLog) { - mLog.i(message); - } - } - - /** - * Dump TetheringManager logs to the specified {@link PrintWriter}. - */ - public void dump(@NonNull PrintWriter pw) { - // dump is thread-safe on SharedLog - mLog.dump(null, pw, null); - - pw.print("subId: "); - pw.println(mTetheringConfiguration.subId); - - dumpStringArray(pw, "tetherableUsbRegexs", - mTetheringConfiguration.tetherableUsbRegexs); - dumpStringArray(pw, "tetherableWifiRegexs", - mTetheringConfiguration.tetherableWifiRegexs); - dumpStringArray(pw, "tetherableBluetoothRegexs", - mTetheringConfiguration.tetherableBluetoothRegexs); - - pw.print("isDunRequired: "); - pw.println(mTetheringConfiguration.isDunRequired); - - pw.print("chooseUpstreamAutomatically: "); - pw.println(mTetheringConfiguration.chooseUpstreamAutomatically); - - dumpStringArray(pw, "legacyDhcpRanges", mTetheringConfiguration.legacyDhcpRanges); - dumpStringArray(pw, "defaultIPv4DNS", mTetheringConfiguration.defaultIPv4DNS); - - dumpStringArray(pw, "provisioningApp", mTetheringConfiguration.provisioningApp); - pw.print("provisioningAppNoUi: "); - pw.println(mTetheringConfiguration.provisioningAppNoUi); - - pw.print("enableLegacyDhcpServer: "); - pw.println(mTetheringConfiguration.enableLegacyDhcpServer); - - pw.println(); - } - - private static void dumpStringArray(@NonNull PrintWriter pw, @NonNull String label, - @Nullable String[] values) { - pw.print(label); - pw.print(": "); - - if (values != null) { - final StringJoiner sj = new StringJoiner(", ", "[", "]"); - for (String value : values) sj.add(value); - - pw.print(sj.toString()); - } else { - pw.print("null"); - } - - pw.println(); + final RequestDispatcher dispatcher = new RequestDispatcher(); + dispatcher.waitForResult(listener -> { + try { + mConnector.stopAllTethering(callerPkg, listener); + } catch (RemoteException e) { + throw new IllegalStateException(e); + } + }); } } diff --git a/Tethering/jarjar-rules.txt b/Tethering/jarjar-rules.txt new file mode 100644 index 0000000000..dd9eab74e8 --- /dev/null +++ b/Tethering/jarjar-rules.txt @@ -0,0 +1,15 @@ +# These must be kept in sync with the framework-tethering-shared-srcs filegroup. +# If there are files in that filegroup that do not appear here, the classes in the +# module will be overwritten by the ones in the framework. +# Don't jar-jar the entire package because tethering still use some internal classes +# (like TrafficStatsConstants in com.android.internal.util) +# TODO: simply these when tethering is built as system_current. +rule com.android.internal.util.BitUtils* com.android.networkstack.tethering.util.BitUtils@1 +rule com.android.internal.util.IndentingPrintWriter.java* com.android.networkstack.tethering.util.IndentingPrintWriter.java@1 +rule com.android.internal.util.IState.java* com.android.networkstack.tethering.util.IState.java@1 +rule com.android.internal.util.MessageUtils* com.android.networkstack.tethering.util.MessageUtils@1 +rule com.android.internal.util.Preconditions* com.android.networkstack.tethering.util.Preconditions@1 +rule com.android.internal.util.State* com.android.networkstack.tethering.util.State@1 +rule com.android.internal.util.StateMachine* com.android.networkstack.tethering.util.StateMachine@1 + +rule android.net.LocalLog* com.android.networkstack.tethering.LocalLog@1 diff --git a/Tethering/proguard.flags b/Tethering/proguard.flags index 77fc024a87..1f83a66382 100644 --- a/Tethering/proguard.flags +++ b/Tethering/proguard.flags @@ -1 +1,9 @@ -#TBD +# Keep class's integer static field for MessageUtils to parsing their name. +-keep class com.android.server.connectivity.tethering.Tethering$TetherMasterSM { + static final int CMD_*; + static final int EVENT_*; +} + +-keepclassmembers class android.net.ip.IpServer { + static final int CMD_*; +} diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java index ff3d7bc6fd..8fde52040e 100644 --- a/Tethering/src/android/net/ip/IpServer.java +++ b/Tethering/src/android/net/ip/IpServer.java @@ -30,7 +30,6 @@ import android.net.InterfaceConfiguration; import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties; -import android.net.NetworkStackClient; import android.net.RouteInfo; import android.net.dhcp.DhcpServerCallbacks; import android.net.dhcp.DhcpServingParamsParcel; @@ -122,7 +121,7 @@ public class IpServer extends StateMachine { * @param state one of STATE_* * @param lastError one of ConnectivityManager.TETHER_ERROR_* */ - public void updateInterfaceState(IpServer who, int state, int lastError) {} + public void updateInterfaceState(IpServer who, int state, int lastError) { } /** * Notify that |who| has new LinkProperties. @@ -130,11 +129,11 @@ public class IpServer extends StateMachine { * @param who the calling instance of IpServer * @param newLp the new LinkProperties to report */ - public void updateLinkProperties(IpServer who, LinkProperties newLp) {} + public void updateLinkProperties(IpServer who, LinkProperties newLp) { } } /** Capture IpServer dependencies, for injection. */ - public static class Dependencies { + public abstract static class Dependencies { /** Create a RouterAdvertisementDaemon instance to be used by IpServer.*/ public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) { return new RouterAdvertisementDaemon(ifParams); @@ -149,13 +148,9 @@ public class IpServer extends StateMachine { return NetdService.getInstance(); } - /** - * Create a DhcpServer instance to be used by IpServer. - */ - public void makeDhcpServer(String ifName, DhcpServingParamsParcel params, - DhcpServerCallbacks cb) { - NetworkStackClient.getInstance().makeDhcpServer(ifName, params, cb); - } + /** Create a DhcpServer instance to be used by IpServer. */ + public abstract void makeDhcpServer(String ifName, DhcpServingParamsParcel params, + DhcpServerCallbacks cb); } private static final int BASE_IFACE = Protocol.BASE_TETHERING + 100; diff --git a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java index c4b360d929..a68b9b261e 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java +++ b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java @@ -16,6 +16,7 @@ package com.android.server.connectivity.tethering; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.hardware.usb.UsbManager.USB_CONFIGURED; import static android.hardware.usb.UsbManager.USB_CONNECTED; import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS; @@ -63,7 +64,7 @@ import android.hardware.usb.UsbManager; import android.net.INetd; import android.net.INetworkPolicyManager; import android.net.INetworkStatsService; -import android.net.ITetherInternalCallback; +import android.net.ITetheringEventCallback; import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties; @@ -89,6 +90,7 @@ import android.os.Handler; import android.os.INetworkManagementService; import android.os.Looper; import android.os.Message; +import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.UserHandle; @@ -103,7 +105,6 @@ import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; -import com.android.internal.util.DumpUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.MessageUtils; import com.android.internal.util.Protocol; @@ -162,6 +163,8 @@ public class Tethering { } private final SharedLog mLog = new SharedLog(TAG); + private final RemoteCallbackList mTetheringEventCallbacks = + new RemoteCallbackList<>(); // used to synchronize public access to members private final Object mPublicSync; @@ -188,8 +191,8 @@ public class Tethering { private final NetdCallback mNetdCallback; private final UserRestrictionActionListener mTetheringRestriction; private int mActiveDataSubId = INVALID_SUBSCRIPTION_ID; - // All the usage of mTetherInternalCallback should run in the same thread. - private ITetherInternalCallback mTetherInternalCallback = null; + // All the usage of mTetheringEventCallback should run in the same thread. + private ITetheringEventCallback mTetheringEventCallback = null; private volatile TetheringConfiguration mConfig; private InterfaceSet mCurrentUpstreamIfaceSet; @@ -573,6 +576,11 @@ public class Tethering { } } + boolean isTetherProvisioningRequired() { + final TetheringConfiguration cfg = mConfig; + return mEntitlementMgr.isTetherProvisioningRequired(cfg); + } + // TODO: Figure out how to update for local hotspot mode interfaces. private void sendTetherStateChangedBroadcast() { if (!mDeps.isTetheringSupported()) return; @@ -588,7 +596,7 @@ public class Tethering { boolean bluetoothTethered = false; final TetheringConfiguration cfg = mConfig; - final TetherStatesParcel mTetherStatesParcel = new TetherStatesParcel(); + mTetherStatesParcel = new TetherStatesParcel(); synchronized (mPublicSync) { for (int i = 0; i < mTetherStates.size(); i++) { @@ -1796,47 +1804,67 @@ public class Tethering { } /** Register tethering event callback */ - void registerTetherInternalCallback(ITetherInternalCallback callback) { + void registerTetheringEventCallback(ITetheringEventCallback callback) { mHandler.post(() -> { - mTetherInternalCallback = callback; + mTetheringEventCallbacks.register(callback); try { - mTetherInternalCallback.onCallbackCreated(mTetherUpstream, - mConfig.toStableParcelable(), mTetherStatesParcel); + callback.onCallbackStarted(mTetherUpstream, mConfig.toStableParcelable(), + mTetherStatesParcel); } catch (RemoteException e) { // Not really very much to do here. } }); } - private void reportUpstreamChanged(Network network) { - // Don't need to synchronized mTetherInternalCallback because all the usage of this variable - // should run at the same thread. - if (mTetherInternalCallback == null) return; + /** Unregister tethering event callback */ + void unregisterTetheringEventCallback(ITetheringEventCallback callback) { + mHandler.post(() -> { + mTetheringEventCallbacks.unregister(callback); + }); + } + private void reportUpstreamChanged(Network network) { + final int length = mTetheringEventCallbacks.beginBroadcast(); try { - mTetherInternalCallback.onUpstreamChanged(network); - } catch (RemoteException e) { - // Not really very much to do here. + for (int i = 0; i < length; i++) { + try { + mTetheringEventCallbacks.getBroadcastItem(i).onUpstreamChanged(network); + } catch (RemoteException e) { + // Not really very much to do here. + } + } + } finally { + mTetheringEventCallbacks.finishBroadcast(); } } private void reportConfigurationChanged(TetheringConfigurationParcel config) { - if (mTetherInternalCallback == null) return; - + final int length = mTetheringEventCallbacks.beginBroadcast(); try { - mTetherInternalCallback.onConfigurationChanged(config); - } catch (RemoteException e) { - // Not really very much to do here. + for (int i = 0; i < length; i++) { + try { + mTetheringEventCallbacks.getBroadcastItem(i).onConfigurationChanged(config); + } catch (RemoteException e) { + // Not really very much to do here. + } + } + } finally { + mTetheringEventCallbacks.finishBroadcast(); } } private void reportTetherStateChanged(TetherStatesParcel states) { - if (mTetherInternalCallback == null) return; - + final int length = mTetheringEventCallbacks.beginBroadcast(); try { - mTetherInternalCallback.onTetherStatesChanged(states); - } catch (RemoteException e) { - // Not really very much to do here. + for (int i = 0; i < length; i++) { + try { + mTetheringEventCallbacks.getBroadcastItem(i).onTetherStatesChanged(states); + } catch (RemoteException e) { + // Not really very much to do here. + } + } + } finally { + mTetheringEventCallbacks.finishBroadcast(); } } @@ -1844,7 +1872,11 @@ public class Tethering { // Binder.java closes the resource for us. @SuppressWarnings("resource") final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); - if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) + != PERMISSION_GRANTED) { + pw.println("Permission Denial: can't dump."); + return; + } pw.println("Tethering:"); pw.increaseIndent(); diff --git a/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java b/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java index 0ba84127d8..b16b3294a1 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java +++ b/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java @@ -39,7 +39,7 @@ import java.util.ArrayList; * * @hide */ -public class TetheringDependencies { +public abstract class TetheringDependencies { /** * Get a reference to the offload hardware interface to be used by tethering. */ @@ -66,9 +66,7 @@ public class TetheringDependencies { /** * Get dependencies to be used by IpServer. */ - public IpServer.Dependencies getIpServerDependencies() { - return new IpServer.Dependencies(); - } + public abstract IpServer.Dependencies getIpServerDependencies(); /** * Indicates whether tethering is supported on the device. @@ -80,9 +78,7 @@ public class TetheringDependencies { /** * Get the NetworkRequest that should be fulfilled by the default network. */ - public NetworkRequest getDefaultNetworkRequest() { - return null; - } + public abstract NetworkRequest getDefaultNetworkRequest(); /** * Get a reference to the EntitlementManager to be used by tethering. @@ -138,14 +134,10 @@ public class TetheringDependencies { /** * Get tethering thread looper. */ - public Looper getTetheringLooper() { - return null; - } + public abstract Looper getTetheringLooper(); /** * Get Context of TetheringSerice. */ - public Context getContext() { - return null; - } + public abstract Context getContext(); } diff --git a/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java b/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java index 456f2f7f27..ba30845e63 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java +++ b/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java @@ -16,20 +16,36 @@ package com.android.server.connectivity.tethering; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.net.TetheringManager.TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION; +import static android.net.TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION; +import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; +import static android.net.TetheringManager.TETHER_ERROR_UNSUPPORTED; + import android.app.Service; import android.content.Context; import android.content.Intent; import android.net.ConnectivityManager; -import android.net.ITetherInternalCallback; +import android.net.IIntResultListener; +import android.net.INetworkStackConnector; import android.net.ITetheringConnector; +import android.net.ITetheringEventCallback; import android.net.NetworkRequest; +import android.net.dhcp.DhcpServerCallbacks; +import android.net.dhcp.DhcpServingParamsParcel; +import android.net.ip.IpServer; import android.net.util.SharedLog; +import android.os.Binder; import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; +import android.os.RemoteException; import android.os.ResultReceiver; +import android.os.ServiceManager; import android.os.SystemProperties; +import android.os.UserManager; import android.provider.Settings; +import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -52,6 +68,7 @@ public class TetheringService extends Service { private Context mContext; private TetheringDependencies mDeps; private Tethering mTethering; + private UserManager mUserManager; @Override public void onCreate() { @@ -59,6 +76,7 @@ public class TetheringService extends Service { mDeps = getTetheringDependencies(); mContext = mDeps.getContext(); mTethering = makeTethering(mDeps); + mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); } /** @@ -74,7 +92,7 @@ public class TetheringService extends Service { */ private synchronized IBinder makeConnector() { if (mConnector == null) { - mConnector = new TetheringConnector(mTethering); + mConnector = new TetheringConnector(mTethering, TetheringService.this); } return mConnector; } @@ -87,55 +105,197 @@ public class TetheringService extends Service { } private static class TetheringConnector extends ITetheringConnector.Stub { - private final Tethering mService; + private final TetheringService mService; + private final Tethering mTethering; - TetheringConnector(Tethering tether) { - mService = tether; + TetheringConnector(Tethering tether, TetheringService service) { + mTethering = tether; + mService = service; } @Override - public void tether(String iface) { - mService.tether(iface); + public void tether(String iface, String callerPkg, IIntResultListener listener) { + if (checkAndNotifyCommonError(callerPkg, listener)) return; + + try { + listener.onResult(mTethering.tether(iface)); + } catch (RemoteException e) { } } @Override - public void untether(String iface) { - mService.untether(iface); + public void untether(String iface, String callerPkg, IIntResultListener listener) { + if (checkAndNotifyCommonError(callerPkg, listener)) return; + + try { + listener.onResult(mTethering.untether(iface)); + } catch (RemoteException e) { } } @Override - public void setUsbTethering(boolean enable) { - mService.setUsbTethering(enable); + public void setUsbTethering(boolean enable, String callerPkg, IIntResultListener listener) { + if (checkAndNotifyCommonError(callerPkg, listener)) return; + + try { + listener.onResult(mTethering.setUsbTethering(enable)); + } catch (RemoteException e) { } } @Override - public void startTethering(int type, ResultReceiver receiver, boolean showProvisioningUi) { - mService.startTethering(type, receiver, showProvisioningUi); + public void startTethering(int type, ResultReceiver receiver, boolean showProvisioningUi, + String callerPkg) { + if (checkAndNotifyCommonError(callerPkg, receiver)) return; + + mTethering.startTethering(type, receiver, showProvisioningUi); } @Override - public void stopTethering(int type) { - mService.stopTethering(type); + public void stopTethering(int type, String callerPkg, IIntResultListener listener) { + if (checkAndNotifyCommonError(callerPkg, listener)) return; + + try { + mTethering.stopTethering(type); + listener.onResult(TETHER_ERROR_NO_ERROR); + } catch (RemoteException e) { } } @Override public void requestLatestTetheringEntitlementResult(int type, ResultReceiver receiver, - boolean showEntitlementUi) { - mService.requestLatestTetheringEntitlementResult(type, receiver, showEntitlementUi); + boolean showEntitlementUi, String callerPkg) { + if (checkAndNotifyCommonError(callerPkg, receiver)) return; + + mTethering.requestLatestTetheringEntitlementResult(type, receiver, showEntitlementUi); } @Override - public void registerTetherInternalCallback(ITetherInternalCallback callback) { - mService.registerTetherInternalCallback(callback); + public void registerTetheringEventCallback(ITetheringEventCallback callback, + String callerPkg) { + try { + if (!mService.hasTetherAccessPermission()) { + callback.onCallbackStopped(TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION); + return; + } + mTethering.registerTetheringEventCallback(callback); + } catch (RemoteException e) { } } + + @Override + public void unregisterTetheringEventCallback(ITetheringEventCallback callback, + String callerPkg) { + try { + if (!mService.hasTetherAccessPermission()) { + callback.onCallbackStopped(TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION); + return; + } + mTethering.unregisterTetheringEventCallback(callback); + } catch (RemoteException e) { } + } + + @Override + public void stopAllTethering(String callerPkg, IIntResultListener listener) { + if (checkAndNotifyCommonError(callerPkg, listener)) return; + + try { + mTethering.untetherAll(); + listener.onResult(TETHER_ERROR_NO_ERROR); + } catch (RemoteException e) { } + } + + @Override + public void isTetheringSupported(String callerPkg, IIntResultListener listener) { + if (checkAndNotifyCommonError(callerPkg, listener)) return; + + try { + listener.onResult(TETHER_ERROR_NO_ERROR); + } catch (RemoteException e) { } + } + + @Override + protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer, + @Nullable String[] args) { + mTethering.dump(fd, writer, args); + } + + private boolean checkAndNotifyCommonError(String callerPkg, IIntResultListener listener) { + try { + if (!mService.hasTetherChangePermission(callerPkg)) { + listener.onResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); + return true; + } + if (!mService.isTetheringSupported()) { + listener.onResult(TETHER_ERROR_UNSUPPORTED); + return true; + } + } catch (RemoteException e) { + return true; + } + + return false; + } + + private boolean checkAndNotifyCommonError(String callerPkg, ResultReceiver receiver) { + if (!mService.hasTetherChangePermission(callerPkg)) { + receiver.send(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION, null); + return true; + } + if (!mService.isTetheringSupported()) { + receiver.send(TETHER_ERROR_UNSUPPORTED, null); + return true; + } + + return false; + } + } - @Override - protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer, - @Nullable String[] args) { - mTethering.dump(fd, writer, args); + // if ro.tether.denied = true we default to no tethering + // gservices could set the secure setting to 1 though to enable it on a build where it + // had previously been turned off. + private boolean isTetheringSupported() { + final int defaultVal = + SystemProperties.get("ro.tether.denied").equals("true") ? 0 : 1; + final boolean tetherSupported = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.TETHER_SUPPORTED, defaultVal) != 0; + final boolean tetherEnabledInSettings = tetherSupported + && !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING); + + return tetherEnabledInSettings && mTethering.hasTetherableConfiguration(); } + private boolean hasTetherChangePermission(String callerPkg) { + if (checkCallingOrSelfPermission( + android.Manifest.permission.TETHER_PRIVILEGED) == PERMISSION_GRANTED) { + return true; + } + + if (mTethering.isTetherProvisioningRequired()) return false; + + + int uid = Binder.getCallingUid(); + // If callerPkg's uid is not same as Binder.getCallingUid(), + // checkAndNoteWriteSettingsOperation will return false and the operation will be denied. + if (Settings.checkAndNoteWriteSettingsOperation(mContext, uid, callerPkg, + false /* throwException */)) { + return true; + } + + return false; + } + + private boolean hasTetherAccessPermission() { + if (checkCallingOrSelfPermission( + android.Manifest.permission.TETHER_PRIVILEGED) == PERMISSION_GRANTED) { + return true; + } + + if (checkCallingOrSelfPermission( + android.Manifest.permission.ACCESS_NETWORK_STATE) == PERMISSION_GRANTED) { + return true; + } + + return false; + } + + /** * An injection method for testing. */ @@ -159,20 +319,55 @@ public class TetheringService extends Service { @Override public boolean isTetheringSupported() { - int defaultVal = - SystemProperties.get("ro.tether.denied").equals("true") ? 0 : 1; - boolean tetherSupported = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.TETHER_SUPPORTED, defaultVal) != 0; - return tetherSupported; + return TetheringService.this.isTetheringSupported(); } @Override public Context getContext() { return TetheringService.this; } + + @Override + public IpServer.Dependencies getIpServerDependencies() { + return new IpServer.Dependencies() { + @Override + public void makeDhcpServer(String ifName, DhcpServingParamsParcel params, + DhcpServerCallbacks cb) { + try { + final INetworkStackConnector service = getNetworkStackConnector(); + if (service == null) return; + + service.makeDhcpServer(ifName, params, cb); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + }; + } + + // TODO: replace this by NetworkStackClient#getRemoteConnector after refactoring + // networkStackClient. + static final int NETWORKSTACK_TIMEOUT_MS = 60_000; + private INetworkStackConnector getNetworkStackConnector() { + IBinder connector; + try { + final long before = System.currentTimeMillis(); + while ((connector = ServiceManager.getService( + Context.NETWORK_STACK_SERVICE)) == null) { + if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) { + Log.wtf(TAG, "Timeout, fail to get INetworkStackConnector"); + return null; + } + Thread.sleep(200); + } + } catch (InterruptedException e) { + Log.wtf(TAG, "Interrupted, fail to get INetworkStackConnector"); + return null; + } + return INetworkStackConnector.Stub.asInterface(connector); + } }; } - return mDeps; } } diff --git a/Tethering/tests/unit/Android.bp b/Tethering/tests/unit/Android.bp index 5b018df21a..81a0548cd7 100644 --- a/Tethering/tests/unit/Android.bp +++ b/Tethering/tests/unit/Android.bp @@ -33,10 +33,12 @@ android_test { "android.test.runner", "android.test.base", "android.test.mock", + "framework-tethering", ], jni_libs: [ // For mockito extended "libdexmakerjvmtiagent", "libstaticjvmtiagent", ], + jarjar_rules: "jarjar-rules.txt", } diff --git a/Tethering/tests/unit/jarjar-rules.txt b/Tethering/tests/unit/jarjar-rules.txt new file mode 100644 index 0000000000..64fdebd927 --- /dev/null +++ b/Tethering/tests/unit/jarjar-rules.txt @@ -0,0 +1,11 @@ +# Don't jar-jar the entire package because this test use some +# internal classes (like ArrayUtils in com.android.internal.util) +rule com.android.internal.util.BitUtils* com.android.networkstack.tethering.util.BitUtils@1 +rule com.android.internal.util.IndentingPrintWriter.java* com.android.networkstack.tethering.util.IndentingPrintWriter.java@1 +rule com.android.internal.util.IState.java* com.android.networkstack.tethering.util.IState.java@1 +rule com.android.internal.util.MessageUtils* com.android.networkstack.tethering.util.MessageUtils@1 +rule com.android.internal.util.Preconditions* com.android.networkstack.tethering.util.Preconditions@1 +rule com.android.internal.util.State* com.android.networkstack.tethering.util.State@1 +rule com.android.internal.util.StateMachine* com.android.networkstack.tethering.util.StateMachine@1 + +rule android.net.LocalLog* com.android.networkstack.tethering.LocalLog@1 diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java index cdbc54106c..31ed8238b6 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java @@ -72,7 +72,7 @@ import android.hardware.usb.UsbManager; import android.net.INetd; import android.net.INetworkPolicyManager; import android.net.INetworkStatsService; -import android.net.ITetherInternalCallback; +import android.net.ITetheringEventCallback; import android.net.InterfaceConfiguration; import android.net.IpPrefix; import android.net.LinkAddress; @@ -81,6 +81,7 @@ import android.net.MacAddress; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkInfo; +import android.net.NetworkRequest; import android.net.NetworkState; import android.net.NetworkUtils; import android.net.RouteInfo; @@ -167,6 +168,7 @@ public class TetheringTest { @Mock private IDhcpServer mDhcpServer; @Mock private INetd mNetd; @Mock private UserManager mUserManager; + @Mock private NetworkRequest mNetworkRequest; private final MockIpServerDependencies mIpServerDependencies = spy(new MockIpServerDependencies()); @@ -310,6 +312,11 @@ public class TetheringTest { return mIpServerDependencies; } + @Override + public NetworkRequest getDefaultNetworkRequest() { + return mNetworkRequest; + } + @Override public boolean isTetheringSupported() { mIsTetheringSupportedCalls++; @@ -1039,7 +1046,7 @@ public class TetheringTest { expectedInteractionsWithShowNotification); } - private class TestTetherInternalCallback extends ITetherInternalCallback.Stub { + private class TestTetheringEventCallback extends ITetheringEventCallback.Stub { private final ArrayList mActualUpstreams = new ArrayList<>(); private final ArrayList mTetheringConfigs = new ArrayList<>(); @@ -1100,13 +1107,16 @@ public class TetheringTest { } @Override - public void onCallbackCreated(Network network, TetheringConfigurationParcel config, + public void onCallbackStarted(Network network, TetheringConfigurationParcel config, TetherStatesParcel states) { mActualUpstreams.add(network); mTetheringConfigs.add(config); mTetherStates.add(states); } + @Override + public void onCallbackStopped(int errorCode) { } + public void assertNoUpstreamChangeCallback() { assertTrue(mActualUpstreams.isEmpty()); } @@ -1115,10 +1125,20 @@ public class TetheringTest { assertTrue(mTetheringConfigs.isEmpty()); } + public void assertNoStateChangeCallback() { + assertTrue(mTetherStates.isEmpty()); + } + public void assertStateChangeCallback() { assertFalse(mTetherStates.isEmpty()); } + public void assertNoCallback() { + assertNoUpstreamChangeCallback(); + assertNoConfigChangeCallback(); + assertNoStateChangeCallback(); + } + private void assertTetherConfigParcelEqual(@NonNull TetheringConfigurationParcel actual, @NonNull TetheringConfigurationParcel expect) { assertEquals(actual.subId, expect.subId); @@ -1139,18 +1159,19 @@ public class TetheringTest { } @Test - public void testRegisterTetherInternalCallback() throws Exception { - TestTetherInternalCallback callback = new TestTetherInternalCallback(); + public void testRegisterTetheringEventCallback() throws Exception { + TestTetheringEventCallback callback = new TestTetheringEventCallback(); + TestTetheringEventCallback callback2 = new TestTetheringEventCallback(); // 1. Register one callback before running any tethering. - mTethering.registerTetherInternalCallback(callback); + mTethering.registerTetheringEventCallback(callback); mLooper.dispatchAll(); callback.expectUpstreamChanged(new Network[] {null}); callback.expectConfigurationChanged( mTethering.getTetheringConfiguration().toStableParcelable()); TetherStatesParcel tetherState = callback.pollTetherStatesChanged(); assertEquals(tetherState, null); - // 2. Enable wifi tethering + // 2. Enable wifi tethering. NetworkState upstreamState = buildMobileDualStackUpstreamState(); when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState); when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())) @@ -1168,14 +1189,26 @@ public class TetheringTest { assertArrayEquals(tetherState.tetheredList, new String[] {TEST_WLAN_IFNAME}); callback.expectUpstreamChanged(upstreamState.network); - // 3. Disable wifi tethering. + // 3. Register second callback. + mTethering.registerTetheringEventCallback(callback2); + mLooper.dispatchAll(); + callback2.expectUpstreamChanged(upstreamState.network); + callback2.expectConfigurationChanged( + mTethering.getTetheringConfiguration().toStableParcelable()); + tetherState = callback2.pollTetherStatesChanged(); + assertEquals(tetherState.tetheredList, new String[] {TEST_WLAN_IFNAME}); + + // 4. Unregister first callback and disable wifi tethering + mTethering.unregisterTetheringEventCallback(callback); + mLooper.dispatchAll(); mTethering.stopTethering(TETHERING_WIFI); sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED); mLooper.dispatchAll(); - tetherState = callback.pollTetherStatesChanged(); + tetherState = callback2.pollTetherStatesChanged(); assertArrayEquals(tetherState.availableList, new String[] {TEST_WLAN_IFNAME}); mLooper.dispatchAll(); - callback.expectUpstreamChanged(new Network[] {null}); + callback2.expectUpstreamChanged(new Network[] {null}); + callback.assertNoCallback(); } @Test From 64134b6f22a09618a8e87c30f8fa98c463e36a7d Mon Sep 17 00:00:00 2001 From: markchien Date: Fri, 20 Dec 2019 21:58:00 +0800 Subject: [PATCH 012/188] Fix clean InProcessTethering fail problem CleanSpec only respect root of a git project. Remove packages/Tethering/CldanSpec.mk because it never be read. Bug: 144320246 Test: manual build InProcessTethering and observed it is removed after adding new clean step. Change-Id: I58750e4a1fe11018d83da1ebf828d1b3134ec765 --- Tethering/CleanSpec.mk | 58 ------------------------------------------ 1 file changed, 58 deletions(-) delete mode 100644 Tethering/CleanSpec.mk diff --git a/Tethering/CleanSpec.mk b/Tethering/CleanSpec.mk deleted file mode 100644 index 30bdd581d0..0000000000 --- a/Tethering/CleanSpec.mk +++ /dev/null @@ -1,58 +0,0 @@ -# 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. -# - -# If you don't need to do a full clean build but would like to touch -# a file or delete some intermediate files, add a clean step to the end -# of the list. These steps will only be run once, if they haven't been -# run before. -# -# E.g.: -# $(call add-clean-step, touch -c external/sqlite/sqlite3.h) -# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates) -# -# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with -# files that are missing or have been moved. -# -# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory. -# Use $(OUT_DIR) to refer to the "out" directory. -# -# If you need to re-do something that's already mentioned, just copy -# the command and add it to the bottom of the list. E.g., if a change -# that you made last week required touching a file and a change you -# made today requires touching the same file, just copy the old -# touch step and add it to the end of the list. -# -# ***************************************************************** -# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THE BANNER -# ***************************************************************** - -# For example: -#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates) -#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates) -#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f) -#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*) - -$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/Tethering) -$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/InProcessTethering) -$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/InProcessTethering*) -$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/InProcessTethering*) -$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system_other/system/priv-app/InProcessTethering) -$(call add-clean-step, rm -rf $(PRODUCT_OUT)/apex/com.android.tethering) -$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/apex/com.android.tethering.apex) -$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/ETC/com.android.tethering*) - -# ****************************************************************** -# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER -# ****************************************************************** From 2e1ad6ef0098e9f6cd47011ff703f91eb6fda4c6 Mon Sep 17 00:00:00 2001 From: Artur Satayev Date: Tue, 10 Dec 2019 17:47:53 +0000 Subject: [PATCH 013/188] Use new UnsupportedAppUsage annotation. Existing annotations in libcore/ and frameworks/ will deleted after the migration. This also means that any java library that compiles @UnsupportedAppUsage requires a direct dependency on "unsupportedappusage" java_library. Bug: 145132366 Test: m && diff unsupportedappusage_index.csv Change-Id: I288969b0c22fa3a63bc2e71bb5009fe4a927e154 --- Tethering/Android.bp | 1 + 1 file changed, 1 insertion(+) diff --git a/Tethering/Android.bp b/Tethering/Android.bp index 596f62fad2..0be853a81f 100644 --- a/Tethering/Android.bp +++ b/Tethering/Android.bp @@ -32,6 +32,7 @@ java_defaults { ], libs: [ "framework-tethering", + "unsupportedappusage", ], manifest: "AndroidManifestBase.xml", From dac2826929e4235a67f9d00db869f4e4d7023747 Mon Sep 17 00:00:00 2001 From: markchien Date: Tue, 7 Jan 2020 14:43:17 +0800 Subject: [PATCH 014/188] [Tether09] Use INetd to call netd directly - Using INetd to communicate with netd directly instead of using NetworkManagementService which is a wrapper of INetd and don't have plan to be updatable. - Also replace InterfaceConfiguration by InterfaceController. - Remove redundant interface flags. Only set interface up/down flag to netd because netd only use these two flags for INetd#interfaceSetCfg. - Note that tethering still use NetworkManagementService to register tethering stats provider and it would also be replaced with other way in follow up change. Bug: 136040414 Test: -build, flash, boot -atest TetheringTests Change-Id: I4ab0ad387d4bd1773ff94d3b380c1720df07f8d5 --- Tethering/Android.bp | 2 +- Tethering/src/android/net/ip/IpServer.java | 192 ++++++++-------- .../connectivity/tethering/Tethering.java | 76 +++---- .../unit/src/android/net/ip/IpServerTest.java | 190 +++++++++------- .../connectivity/tethering/TetheringTest.java | 209 +++++++++--------- 5 files changed, 341 insertions(+), 328 deletions(-) diff --git a/Tethering/Android.bp b/Tethering/Android.bp index 0be853a81f..797b713d86 100644 --- a/Tethering/Android.bp +++ b/Tethering/Android.bp @@ -20,7 +20,7 @@ java_defaults { srcs: [ "src/**/*.java", ":framework-tethering-shared-srcs", - ":net-module-utils-srcs", + ":tethering-module-utils-srcs", ":services-tethering-shared-srcs", ], static_libs: [ diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java index abfb33c7af..cf8769e562 100644 --- a/Tethering/src/android/net/ip/IpServer.java +++ b/Tethering/src/android/net/ip/IpServer.java @@ -28,7 +28,6 @@ import android.net.ConnectivityManager; import android.net.INetd; import android.net.INetworkStackStatusCallback; import android.net.INetworkStatsService; -import android.net.InterfaceConfiguration; import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties; @@ -38,11 +37,11 @@ import android.net.dhcp.DhcpServingParamsParcel; import android.net.dhcp.DhcpServingParamsParcelExt; import android.net.dhcp.IDhcpServer; import android.net.ip.RouterAdvertisementDaemon.RaParams; +import android.net.shared.NetdUtils; +import android.net.shared.RouteUtils; import android.net.util.InterfaceParams; import android.net.util.InterfaceSet; -import android.net.util.NetdService; import android.net.util.SharedLog; -import android.os.INetworkManagementService; import android.os.Looper; import android.os.Message; import android.os.RemoteException; @@ -144,10 +143,6 @@ public class IpServer extends StateMachine { return InterfaceParams.getByName(ifName); } - public INetd getNetdService() { - return NetdService.getInstance(); - } - /** Create a DhcpServer instance to be used by IpServer. */ public abstract void makeDhcpServer(String ifName, DhcpServingParamsParcel params, DhcpServerCallbacks cb); @@ -180,7 +175,6 @@ public class IpServer extends StateMachine { private final State mUnavailableState; private final SharedLog mLog; - private final INetworkManagementService mNMService; private final INetd mNetd; private final INetworkStatsService mStatsService; private final Callback mCallback; @@ -210,15 +204,15 @@ public class IpServer extends StateMachine { private int mDhcpServerStartIndex = 0; private IDhcpServer mDhcpServer; private RaParams mLastRaParams; + private LinkAddress mIpv4Address; public IpServer( String ifaceName, Looper looper, int interfaceType, SharedLog log, - INetworkManagementService nMService, INetworkStatsService statsService, - Callback callback, boolean usingLegacyDhcp, Dependencies deps) { + INetd netd, INetworkStatsService statsService, Callback callback, + boolean usingLegacyDhcp, Dependencies deps) { super(ifaceName, looper); mLog = log.forSubComponent(ifaceName); - mNMService = nMService; - mNetd = deps.getNetdService(); + mNetd = netd; mStatsService = statsService; mCallback = callback; mInterfaceCtrl = new InterfaceController(ifaceName, mNetd, mLog); @@ -347,7 +341,7 @@ public class IpServer extends StateMachine { } }); } catch (RemoteException e) { - e.rethrowFromSystemServer(); + throw new IllegalStateException(e); } }); } @@ -395,7 +389,8 @@ public class IpServer extends StateMachine { }); mDhcpServer = null; } catch (RemoteException e) { - e.rethrowFromSystemServer(); + mLog.e("Error stopping DHCP", e); + // Not much more we can do here } } } @@ -414,85 +409,69 @@ public class IpServer extends StateMachine { // NOTE: All of configureIPv4() will be refactored out of existence // into calls to InterfaceController, shared with startIPv4(). mInterfaceCtrl.clearIPv4Address(); + mIpv4Address = null; } - // TODO: Refactor this in terms of calls to InterfaceController. private boolean configureIPv4(boolean enabled) { if (VDBG) Log.d(TAG, "configureIPv4(" + enabled + ")"); // TODO: Replace this hard-coded information with dynamically selected // config passed down to us by a higher layer IP-coordinating element. - String ipAsString = null; + final Inet4Address srvAddr; int prefixLen = 0; - if (mInterfaceType == ConnectivityManager.TETHERING_USB) { - ipAsString = USB_NEAR_IFACE_ADDR; - prefixLen = USB_PREFIX_LENGTH; - } else if (mInterfaceType == ConnectivityManager.TETHERING_WIFI) { - ipAsString = getRandomWifiIPv4Address(); - prefixLen = WIFI_HOST_IFACE_PREFIX_LENGTH; - } else if (mInterfaceType == ConnectivityManager.TETHERING_WIFI_P2P) { - ipAsString = WIFI_P2P_IFACE_ADDR; - prefixLen = WIFI_P2P_IFACE_PREFIX_LENGTH; - } else { - // BT configures the interface elsewhere: only start DHCP. - final Inet4Address srvAddr = (Inet4Address) parseNumericAddress(BLUETOOTH_IFACE_ADDR); - return configureDhcp(enabled, srvAddr, BLUETOOTH_DHCP_PREFIX_LENGTH); + try { + if (mInterfaceType == ConnectivityManager.TETHERING_USB) { + srvAddr = (Inet4Address) parseNumericAddress(USB_NEAR_IFACE_ADDR); + prefixLen = USB_PREFIX_LENGTH; + } else if (mInterfaceType == ConnectivityManager.TETHERING_WIFI) { + srvAddr = (Inet4Address) parseNumericAddress(getRandomWifiIPv4Address()); + prefixLen = WIFI_HOST_IFACE_PREFIX_LENGTH; + } else if (mInterfaceType == ConnectivityManager.TETHERING_WIFI_P2P) { + srvAddr = (Inet4Address) parseNumericAddress(WIFI_P2P_IFACE_ADDR); + prefixLen = WIFI_P2P_IFACE_PREFIX_LENGTH; + } else { + // BT configures the interface elsewhere: only start DHCP. + // TODO: make all tethering types behave the same way, and delete the bluetooth + // code that calls into NetworkManagementService directly. + srvAddr = (Inet4Address) parseNumericAddress(BLUETOOTH_IFACE_ADDR); + mIpv4Address = new LinkAddress(srvAddr, BLUETOOTH_DHCP_PREFIX_LENGTH); + return configureDhcp(enabled, srvAddr, BLUETOOTH_DHCP_PREFIX_LENGTH); + } + mIpv4Address = new LinkAddress(srvAddr, prefixLen); + } catch (IllegalArgumentException e) { + mLog.e("Error selecting ipv4 address", e); + if (!enabled) stopDhcp(); + return false; } - final LinkAddress linkAddr; - try { - final InterfaceConfiguration ifcg = mNMService.getInterfaceConfig(mIfaceName); - if (ifcg == null) { - mLog.e("Received null interface config"); - return false; - } + final Boolean setIfaceUp; + if (mInterfaceType == ConnectivityManager.TETHERING_WIFI) { + // The WiFi stack has ownership of the interface up/down state. + // It is unclear whether the Bluetooth or USB stacks will manage their own + // state. + setIfaceUp = null; + } else { + setIfaceUp = enabled; + } + if (!mInterfaceCtrl.setInterfaceConfiguration(mIpv4Address, setIfaceUp)) { + mLog.e("Error configuring interface"); + if (!enabled) stopDhcp(); + return false; + } - InetAddress addr = parseNumericAddress(ipAsString); - linkAddr = new LinkAddress(addr, prefixLen); - ifcg.setLinkAddress(linkAddr); - if (mInterfaceType == ConnectivityManager.TETHERING_WIFI) { - // The WiFi stack has ownership of the interface up/down state. - // It is unclear whether the Bluetooth or USB stacks will manage their own - // state. - ifcg.ignoreInterfaceUpDownStatus(); - } else { - if (enabled) { - ifcg.setInterfaceUp(); - } else { - ifcg.setInterfaceDown(); - } - } - ifcg.clearFlag("running"); - - // TODO: this may throw if the interface is already gone. Do proper handling and - // simplify the DHCP server start/stop. - mNMService.setInterfaceConfig(mIfaceName, ifcg); - - if (!configureDhcp(enabled, (Inet4Address) addr, prefixLen)) { - return false; - } - } catch (Exception e) { - mLog.e("Error configuring interface " + e); - if (!enabled) { - try { - // Calling stopDhcp several times is fine - stopDhcp(); - } catch (Exception dhcpError) { - mLog.e("Error stopping DHCP", dhcpError); - } - } + if (!configureDhcp(enabled, srvAddr, prefixLen)) { return false; } // Directly-connected route. - final IpPrefix ipv4Prefix = new IpPrefix(linkAddr.getAddress(), - linkAddr.getPrefixLength()); + final IpPrefix ipv4Prefix = new IpPrefix(mIpv4Address.getAddress(), + mIpv4Address.getPrefixLength()); final RouteInfo route = new RouteInfo(ipv4Prefix, null, null, RTN_UNICAST); if (enabled) { - mLinkProperties.addLinkAddress(linkAddr); + mLinkProperties.addLinkAddress(mIpv4Address); mLinkProperties.addRoute(route); } else { - mLinkProperties.removeLinkAddress(linkAddr); + mLinkProperties.removeLinkAddress(mIpv4Address); mLinkProperties.removeRoute(route); } return true; @@ -584,14 +563,12 @@ public class IpServer extends StateMachine { if (!deprecatedPrefixes.isEmpty()) { final ArrayList toBeRemoved = getLocalRoutesFor(mIfaceName, deprecatedPrefixes); - try { - final int removalFailures = mNMService.removeRoutesFromLocalNetwork(toBeRemoved); - if (removalFailures > 0) { - mLog.e(String.format("Failed to remove %d IPv6 routes from local table.", - removalFailures)); - } - } catch (RemoteException e) { - mLog.e("Failed to remove IPv6 routes from local table: " + e); + // Remove routes from local network. + final int removalFailures = RouteUtils.removeRoutesFromLocalNetwork( + mNetd, toBeRemoved); + if (removalFailures > 0) { + mLog.e(String.format("Failed to remove %d IPv6 routes from local table.", + removalFailures)); } for (RouteInfo route : toBeRemoved) mLinkProperties.removeRoute(route); @@ -608,13 +585,18 @@ public class IpServer extends StateMachine { final ArrayList toBeAdded = getLocalRoutesFor(mIfaceName, addedPrefixes); try { - // It's safe to call addInterfaceToLocalNetwork() even if - // the interface is already in the local_network. Note also - // that adding routes that already exist does not cause an - // error (EEXIST is silently ignored). - mNMService.addInterfaceToLocalNetwork(mIfaceName, toBeAdded); - } catch (Exception e) { - mLog.e("Failed to add IPv6 routes to local table: " + e); + // It's safe to call networkAddInterface() even if + // the interface is already in the local_network. + mNetd.networkAddInterface(INetd.LOCAL_NET_ID, mIfaceName); + try { + // Add routes from local network. Note that adding routes that + // already exist does not cause an error (EEXIST is silently ignored). + RouteUtils.addRoutesToLocalNetwork(mNetd, mIfaceName, toBeAdded); + } catch (IllegalStateException e) { + mLog.e("Failed to add IPv6 routes to local table: " + e); + } + } catch (ServiceSpecificException | RemoteException e) { + mLog.e("Failed to add " + mIfaceName + " to local table: ", e); } for (RouteInfo route : toBeAdded) mLinkProperties.addRoute(route); @@ -762,8 +744,10 @@ public class IpServer extends StateMachine { } try { - mNMService.tetherInterface(mIfaceName); - } catch (Exception e) { + final IpPrefix ipv4Prefix = new IpPrefix(mIpv4Address.getAddress(), + mIpv4Address.getPrefixLength()); + NetdUtils.tetherInterface(mNetd, mIfaceName, ipv4Prefix); + } catch (RemoteException | ServiceSpecificException e) { mLog.e("Error Tethering: " + e); mLastError = ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR; return; @@ -784,8 +768,8 @@ public class IpServer extends StateMachine { stopIPv6(); try { - mNMService.untetherInterface(mIfaceName); - } catch (Exception e) { + NetdUtils.untetherInterface(mNetd, mIfaceName); + } catch (RemoteException | ServiceSpecificException e) { mLastError = ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR; mLog.e("Failed to untether interface: " + e); } @@ -901,17 +885,17 @@ public class IpServer extends StateMachine { // About to tear down NAT; gather remaining statistics. mStatsService.forceUpdate(); } catch (Exception e) { - if (VDBG) Log.e(TAG, "Exception in forceUpdate: " + e.toString()); + mLog.e("Exception in forceUpdate: " + e.toString()); } try { - mNMService.stopInterfaceForwarding(mIfaceName, upstreamIface); - } catch (Exception e) { - if (VDBG) Log.e(TAG, "Exception in removeInterfaceForward: " + e.toString()); + mNetd.ipfwdRemoveInterfaceForward(mIfaceName, upstreamIface); + } catch (RemoteException | ServiceSpecificException e) { + mLog.e("Exception in ipfwdRemoveInterfaceForward: " + e.toString()); } try { - mNMService.disableNat(mIfaceName, upstreamIface); - } catch (Exception e) { - if (VDBG) Log.e(TAG, "Exception in disableNat: " + e.toString()); + mNetd.tetherRemoveForward(mIfaceName, upstreamIface); + } catch (RemoteException | ServiceSpecificException e) { + mLog.e("Exception in disableNat: " + e.toString()); } } @@ -947,10 +931,10 @@ public class IpServer extends StateMachine { for (String ifname : added) { try { - mNMService.enableNat(mIfaceName, ifname); - mNMService.startInterfaceForwarding(mIfaceName, ifname); - } catch (Exception e) { - mLog.e("Exception enabling NAT: " + e); + mNetd.tetherAddForward(mIfaceName, ifname); + mNetd.ipfwdAddInterfaceForward(mIfaceName, ifname); + } catch (RemoteException | ServiceSpecificException e) { + mLog.e("Exception enabling NAT: " + e.toString()); cleanupUpstream(); mLastError = ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR; transitionTo(mInitialState); diff --git a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java index 5b267046a8..67f36dd20e 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java +++ b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java @@ -75,6 +75,7 @@ import android.net.NetworkUtils; import android.net.TetherStatesParcel; import android.net.TetheringConfigurationParcel; import android.net.ip.IpServer; +import android.net.shared.NetdUtils; import android.net.util.BaseNetdUnsolicitedEventListener; import android.net.util.InterfaceSet; import android.net.util.PrefixUtils; @@ -87,12 +88,12 @@ import android.net.wifi.p2p.WifiP2pManager; import android.os.Binder; import android.os.Bundle; import android.os.Handler; -import android.os.INetworkManagementService; import android.os.Looper; import android.os.Message; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ResultReceiver; +import android.os.ServiceSpecificException; import android.os.UserHandle; import android.os.UserManager; import android.telephony.PhoneStateListener; @@ -102,6 +103,9 @@ import android.util.ArrayMap; import android.util.Log; import android.util.SparseArray; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import com.android.internal.annotations.VisibleForTesting; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; @@ -139,6 +143,8 @@ public class Tethering { }; private static final SparseArray sMagicDecoderRing = MessageUtils.findMessageNames(sMessageClasses); + // Keep in sync with NETID_UNSET in system/netd/include/netid_client.h + private static final int NETID_UNSET = 0; private static class TetherState { public final IpServer ipServer; @@ -172,8 +178,6 @@ public class Tethering { private final Context mContext; private final ArrayMap mTetherStates; private final BroadcastReceiver mStateReceiver; - // Stopship: replace mNMService before production. - private final INetworkManagementService mNMService; private final INetworkStatsService mStatsService; private final INetworkPolicyManager mPolicyManager; private final Looper mLooper; @@ -210,7 +214,6 @@ public class Tethering { mLog.mark("Tethering.constructed"); mDeps = deps; mContext = mDeps.getContext(); - mNMService = mDeps.getINetworkManagementService(); mStatsService = mDeps.getINetworkStatsService(); mPolicyManager = mDeps.getINetworkPolicyManager(); mNetd = mDeps.getINetd(mContext); @@ -225,10 +228,9 @@ public class Tethering { mHandler = mTetherMasterSM.getHandler(); mOffloadController = new OffloadController(mHandler, - mDeps.getOffloadHardwareInterface(mHandler, mLog), - mContext.getContentResolver(), mNMService, - mLog); - mUpstreamNetworkMonitor = deps.getUpstreamNetworkMonitor(mContext, mTetherMasterSM, mLog, + mDeps.getOffloadHardwareInterface(mHandler, mLog), mContext.getContentResolver(), + mDeps.getINetworkManagementService(), mLog); + mUpstreamNetworkMonitor = mDeps.getUpstreamNetworkMonitor(mContext, mTetherMasterSM, mLog, TetherMasterSM.EVENT_UPSTREAM_CALLBACK); mForwardedDownstreams = new HashSet<>(); @@ -421,7 +423,6 @@ public class Tethering { } } - void interfaceRemoved(String iface) { if (VDBG) Log.d(TAG, "interfaceRemoved " + iface); synchronized (mPublicSync) { @@ -1022,8 +1023,8 @@ public class Tethering { String[] ifaces = null; try { - ifaces = mNMService.listInterfaces(); - } catch (Exception e) { + ifaces = mNetd.interfaceGetList(); + } catch (RemoteException | ServiceSpecificException e) { Log.e(TAG, "Error listing Interfaces", e); return; } @@ -1282,25 +1283,25 @@ public class Tethering { protected boolean turnOnMasterTetherSettings() { final TetheringConfiguration cfg = mConfig; try { - mNMService.setIpForwardingEnabled(true); - } catch (Exception e) { + mNetd.ipfwdEnableForwarding(TAG); + } catch (RemoteException | ServiceSpecificException e) { mLog.e(e); transitionTo(mSetIpForwardingEnabledErrorState); return false; } + // TODO: Randomize DHCPv4 ranges, especially in hotspot mode. // Legacy DHCP server is disabled if passed an empty ranges array final String[] dhcpRanges = cfg.enableLegacyDhcpServer - ? cfg.legacyDhcpRanges - : new String[0]; + ? cfg.legacyDhcpRanges : new String[0]; try { - // TODO: Find a more accurate method name (startDHCPv4()?). - mNMService.startTethering(dhcpRanges); - } catch (Exception e) { + NetdUtils.tetherStart(mNetd, true /** usingLegacyDnsProxy */, dhcpRanges); + } catch (RemoteException | ServiceSpecificException e) { try { - mNMService.stopTethering(); - mNMService.startTethering(dhcpRanges); - } catch (Exception ee) { + // Stop and retry. + mNetd.tetherStop(); + NetdUtils.tetherStart(mNetd, true /** usingLegacyDnsProxy */, dhcpRanges); + } catch (RemoteException | ServiceSpecificException ee) { mLog.e(ee); transitionTo(mStartTetheringErrorState); return false; @@ -1312,15 +1313,15 @@ public class Tethering { protected boolean turnOffMasterTetherSettings() { try { - mNMService.stopTethering(); - } catch (Exception e) { + mNetd.tetherStop(); + } catch (RemoteException | ServiceSpecificException e) { mLog.e(e); transitionTo(mStopTetheringErrorState); return false; } try { - mNMService.setIpForwardingEnabled(false); - } catch (Exception e) { + mNetd.ipfwdDisableForwarding(TAG); + } catch (RemoteException | ServiceSpecificException e) { mLog.e(e); transitionTo(mSetIpForwardingDisabledErrorState); return false; @@ -1390,12 +1391,13 @@ public class Tethering { // TODO: remove this invocation of NetworkUtils.makeStrings(). dnsServers = NetworkUtils.makeStrings(dnses); } + final int netId = (network != null) ? network.netId : NETID_UNSET; try { - mNMService.setDnsForwarders(network, dnsServers); + mNetd.tetherDnsSet(netId, dnsServers); mLog.log(String.format( "SET DNS forwarders: network=%s dnsServers=%s", network, Arrays.toString(dnsServers))); - } catch (Exception e) { + } catch (RemoteException | ServiceSpecificException e) { // TODO: Investigate how this can fail and what exactly // happens if/when such failures occur. mLog.e("setting DNS forwarders failed, " + e); @@ -1698,8 +1700,8 @@ public class Tethering { Log.e(TAG, "Error in startTethering"); notify(IpServer.CMD_START_TETHERING_ERROR); try { - mNMService.setIpForwardingEnabled(false); - } catch (Exception e) { } + mNetd.ipfwdDisableForwarding(TAG); + } catch (RemoteException | ServiceSpecificException e) { } } } @@ -1709,8 +1711,8 @@ public class Tethering { Log.e(TAG, "Error in stopTethering"); notify(IpServer.CMD_STOP_TETHERING_ERROR); try { - mNMService.setIpForwardingEnabled(false); - } catch (Exception e) { } + mNetd.ipfwdDisableForwarding(TAG); + } catch (RemoteException | ServiceSpecificException e) { } } } @@ -1720,11 +1722,11 @@ public class Tethering { Log.e(TAG, "Error in setDnsForwarders"); notify(IpServer.CMD_SET_DNS_FORWARDERS_ERROR); try { - mNMService.stopTethering(); - } catch (Exception e) { } + mNetd.tetherStop(); + } catch (RemoteException | ServiceSpecificException e) { } try { - mNMService.setIpForwardingEnabled(false); - } catch (Exception e) { } + mNetd.ipfwdDisableForwarding(TAG); + } catch (RemoteException | ServiceSpecificException e) { } } } @@ -1884,7 +1886,7 @@ public class Tethering { } } - void dump(FileDescriptor fd, PrintWriter writer, String[] args) { + void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer, @Nullable String[] args) { // Binder.java closes the resource for us. @SuppressWarnings("resource") final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); @@ -2065,7 +2067,7 @@ public class Tethering { mLog.log("adding TetheringInterfaceStateMachine for: " + iface); final TetherState tetherState = new TetherState( - new IpServer(iface, mLooper, interfaceType, mLog, mNMService, mStatsService, + new IpServer(iface, mLooper, interfaceType, mLog, mNetd, mStatsService, makeControlCallback(), mConfig.enableLegacyDhcpServer, mDeps.getIpServerDependencies())); mTetherStates.put(iface, tetherState); diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java index fd2f708aea..f4a60843fa 100644 --- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java +++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java @@ -23,6 +23,7 @@ import static android.net.ConnectivityManager.TETHERING_WIFI_P2P; import static android.net.ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR; import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR; import static android.net.ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR; +import static android.net.INetd.IF_STATE_UP; import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS; import static android.net.ip.IpServer.STATE_AVAILABLE; import static android.net.ip.IpServer.STATE_LOCAL_ONLY; @@ -52,7 +53,7 @@ import static org.mockito.Mockito.when; import android.net.INetd; import android.net.INetworkStatsService; -import android.net.InterfaceConfiguration; +import android.net.InterfaceConfigurationParcel; import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties; @@ -64,7 +65,6 @@ import android.net.dhcp.IDhcpServerCallbacks; import android.net.util.InterfaceParams; import android.net.util.InterfaceSet; import android.net.util.SharedLog; -import android.os.INetworkManagementService; import android.os.RemoteException; import android.os.test.TestLooper; import android.text.TextUtils; @@ -89,6 +89,8 @@ public class IpServerTest { private static final String IFACE_NAME = "testnet1"; private static final String UPSTREAM_IFACE = "upstream0"; private static final String UPSTREAM_IFACE2 = "upstream1"; + private static final String BLUETOOTH_IFACE_ADDR = "192.168.42.1"; + private static final int BLUETOOTH_DHCP_PREFIX_LENGTH = 24; private static final int DHCP_LEASE_TIME_SECS = 3600; private static final InterfaceParams TEST_IFACE_PARAMS = new InterfaceParams( @@ -96,11 +98,9 @@ public class IpServerTest { private static final int MAKE_DHCPSERVER_TIMEOUT_MS = 1000; - @Mock private INetworkManagementService mNMService; @Mock private INetd mNetd; @Mock private INetworkStatsService mStatsService; @Mock private IpServer.Callback mCallback; - @Mock private InterfaceConfiguration mInterfaceConfiguration; @Mock private SharedLog mSharedLog; @Mock private IDhcpServer mDhcpServer; @Mock private RouterAdvertisementDaemon mRaDaemon; @@ -112,6 +112,7 @@ public class IpServerTest { private final ArgumentCaptor mLinkPropertiesCaptor = ArgumentCaptor.forClass(LinkProperties.class); private IpServer mIpServer; + private InterfaceConfigurationParcel mInterfaceConfiguration; private void initStateMachine(int interfaceType) throws Exception { initStateMachine(interfaceType, false /* usingLegacyDhcp */); @@ -131,17 +132,20 @@ public class IpServerTest { }).when(mDependencies).makeDhcpServer(any(), mDhcpParamsCaptor.capture(), any()); when(mDependencies.getRouterAdvertisementDaemon(any())).thenReturn(mRaDaemon); when(mDependencies.getInterfaceParams(IFACE_NAME)).thenReturn(TEST_IFACE_PARAMS); - when(mDependencies.getNetdService()).thenReturn(mNetd); - + mInterfaceConfiguration = new InterfaceConfigurationParcel(); + mInterfaceConfiguration.flags = new String[0]; + if (interfaceType == TETHERING_BLUETOOTH) { + mInterfaceConfiguration.ipv4Addr = BLUETOOTH_IFACE_ADDR; + mInterfaceConfiguration.prefixLength = BLUETOOTH_DHCP_PREFIX_LENGTH; + } mIpServer = new IpServer( - IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, - mNMService, mStatsService, mCallback, usingLegacyDhcp, mDependencies); + IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd, mStatsService, + mCallback, usingLegacyDhcp, mDependencies); mIpServer.start(); // Starting the state machine always puts us in a consistent state and notifies // the rest of the world that we've changed from an unknown to available state. mLooper.dispatchAll(); - reset(mNMService, mStatsService, mCallback); - when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration); + reset(mNetd, mStatsService, mCallback); when(mRaDaemon.start()).thenReturn(true); } @@ -158,8 +162,7 @@ public class IpServerTest { if (upstreamIface != null) { dispatchTetherConnectionChanged(upstreamIface); } - reset(mNMService, mStatsService, mCallback); - when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration); + reset(mNetd, mStatsService, mCallback); } @Before public void setUp() throws Exception { @@ -169,15 +172,14 @@ public class IpServerTest { @Test public void startsOutAvailable() { - mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), - TETHERING_BLUETOOTH, mSharedLog, mNMService, mStatsService, mCallback, - false /* usingLegacyDhcp */, mDependencies); + mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), TETHERING_BLUETOOTH, mSharedLog, + mNetd, mStatsService, mCallback, false /* usingLegacyDhcp */, mDependencies); mIpServer.start(); mLooper.dispatchAll(); verify(mCallback).updateInterfaceState( mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR); verify(mCallback).updateLinkProperties(eq(mIpServer), any(LinkProperties.class)); - verifyNoMoreInteractions(mCallback, mNMService, mStatsService); + verifyNoMoreInteractions(mCallback, mNetd, mStatsService); } @Test @@ -196,7 +198,7 @@ public class IpServerTest { // None of these commands should trigger us to request action from // the rest of the system. dispatchCommand(command); - verifyNoMoreInteractions(mNMService, mStatsService, mCallback); + verifyNoMoreInteractions(mNetd, mStatsService, mCallback); } } @@ -208,7 +210,7 @@ public class IpServerTest { verify(mCallback).updateInterfaceState( mIpServer, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR); verify(mCallback).updateLinkProperties(eq(mIpServer), any(LinkProperties.class)); - verifyNoMoreInteractions(mNMService, mStatsService, mCallback); + verifyNoMoreInteractions(mNetd, mStatsService, mCallback); } @Test @@ -216,13 +218,17 @@ public class IpServerTest { initStateMachine(TETHERING_BLUETOOTH); dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED); - InOrder inOrder = inOrder(mCallback, mNMService); - inOrder.verify(mNMService).tetherInterface(IFACE_NAME); + InOrder inOrder = inOrder(mCallback, mNetd); + inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME); + inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME); + // One for ipv4 route, one for ipv6 link local route. + inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME), + any(), any()); inOrder.verify(mCallback).updateInterfaceState( mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR); inOrder.verify(mCallback).updateLinkProperties( eq(mIpServer), any(LinkProperties.class)); - verifyNoMoreInteractions(mNMService, mStatsService, mCallback); + verifyNoMoreInteractions(mNetd, mStatsService, mCallback); } @Test @@ -230,14 +236,16 @@ public class IpServerTest { initTetheredStateMachine(TETHERING_BLUETOOTH, null); dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED); - InOrder inOrder = inOrder(mNMService, mNetd, mStatsService, mCallback); - inOrder.verify(mNMService).untetherInterface(IFACE_NAME); + InOrder inOrder = inOrder(mNetd, mStatsService, mCallback); + inOrder.verify(mNetd).tetherApplyDnsInterfaces(); + inOrder.verify(mNetd).tetherInterfaceRemove(IFACE_NAME); + inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME); inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName))); inOrder.verify(mCallback).updateInterfaceState( mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR); inOrder.verify(mCallback).updateLinkProperties( eq(mIpServer), any(LinkProperties.class)); - verifyNoMoreInteractions(mNMService, mStatsService, mCallback); + verifyNoMoreInteractions(mNetd, mStatsService, mCallback); } @Test @@ -245,16 +253,19 @@ public class IpServerTest { initStateMachine(TETHERING_USB); dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED); - InOrder inOrder = inOrder(mCallback, mNMService); - inOrder.verify(mNMService).getInterfaceConfig(IFACE_NAME); - inOrder.verify(mNMService).setInterfaceConfig(IFACE_NAME, mInterfaceConfiguration); - inOrder.verify(mNMService).tetherInterface(IFACE_NAME); + InOrder inOrder = inOrder(mCallback, mNetd); + inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> + IFACE_NAME.equals(cfg.ifName) && assertContainsFlag(cfg.flags, IF_STATE_UP))); + inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME); + inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME); + inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME), + any(), any()); inOrder.verify(mCallback).updateInterfaceState( mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR); inOrder.verify(mCallback).updateLinkProperties( eq(mIpServer), mLinkPropertiesCaptor.capture()); assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue()); - verifyNoMoreInteractions(mNMService, mStatsService, mCallback); + verifyNoMoreInteractions(mNetd, mStatsService, mCallback); } @Test @@ -262,16 +273,19 @@ public class IpServerTest { initStateMachine(TETHERING_WIFI_P2P); dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_LOCAL_ONLY); - InOrder inOrder = inOrder(mCallback, mNMService); - inOrder.verify(mNMService).getInterfaceConfig(IFACE_NAME); - inOrder.verify(mNMService).setInterfaceConfig(IFACE_NAME, mInterfaceConfiguration); - inOrder.verify(mNMService).tetherInterface(IFACE_NAME); + InOrder inOrder = inOrder(mCallback, mNetd); + inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> + IFACE_NAME.equals(cfg.ifName) && assertContainsFlag(cfg.flags, IF_STATE_UP))); + inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME); + inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME); + inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME), + any(), any()); inOrder.verify(mCallback).updateInterfaceState( mIpServer, STATE_LOCAL_ONLY, TETHER_ERROR_NO_ERROR); inOrder.verify(mCallback).updateLinkProperties( eq(mIpServer), mLinkPropertiesCaptor.capture()); assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue()); - verifyNoMoreInteractions(mNMService, mStatsService, mCallback); + verifyNoMoreInteractions(mNetd, mStatsService, mCallback); } @Test @@ -281,10 +295,10 @@ public class IpServerTest { // Telling the state machine about its upstream interface triggers // a little more configuration. dispatchTetherConnectionChanged(UPSTREAM_IFACE); - InOrder inOrder = inOrder(mNMService); - inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE); - inOrder.verify(mNMService).startInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE); - verifyNoMoreInteractions(mNMService, mStatsService, mCallback); + InOrder inOrder = inOrder(mNetd); + inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE); + inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE); + verifyNoMoreInteractions(mNetd, mStatsService, mCallback); } @Test @@ -292,49 +306,49 @@ public class IpServerTest { initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE); dispatchTetherConnectionChanged(UPSTREAM_IFACE2); - InOrder inOrder = inOrder(mNMService, mStatsService); + InOrder inOrder = inOrder(mNetd, mStatsService); inOrder.verify(mStatsService).forceUpdate(); - inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE); - inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE); - inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE2); - inOrder.verify(mNMService).startInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2); - verifyNoMoreInteractions(mNMService, mStatsService, mCallback); + inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE); + inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE); + inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2); + inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2); + verifyNoMoreInteractions(mNetd, mStatsService, mCallback); } @Test public void handlesChangingUpstreamNatFailure() throws Exception { initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE); - doThrow(RemoteException.class).when(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE2); + doThrow(RemoteException.class).when(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2); dispatchTetherConnectionChanged(UPSTREAM_IFACE2); - InOrder inOrder = inOrder(mNMService, mStatsService); + InOrder inOrder = inOrder(mNetd, mStatsService); inOrder.verify(mStatsService).forceUpdate(); - inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE); - inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE); - inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE2); + inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE); + inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE); + inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2); inOrder.verify(mStatsService).forceUpdate(); - inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2); - inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE2); + inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2); + inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE2); } @Test public void handlesChangingUpstreamInterfaceForwardingFailure() throws Exception { initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE); - doThrow(RemoteException.class).when(mNMService).startInterfaceForwarding( + doThrow(RemoteException.class).when(mNetd).ipfwdAddInterfaceForward( IFACE_NAME, UPSTREAM_IFACE2); dispatchTetherConnectionChanged(UPSTREAM_IFACE2); - InOrder inOrder = inOrder(mNMService, mStatsService); + InOrder inOrder = inOrder(mNetd, mStatsService); inOrder.verify(mStatsService).forceUpdate(); - inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE); - inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE); - inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE2); - inOrder.verify(mNMService).startInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2); + inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE); + inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE); + inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2); + inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2); inOrder.verify(mStatsService).forceUpdate(); - inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2); - inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE2); + inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2); + inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE2); } @Test @@ -342,17 +356,19 @@ public class IpServerTest { initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE); dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED); - InOrder inOrder = inOrder(mNMService, mNetd, mStatsService, mCallback); + InOrder inOrder = inOrder(mNetd, mStatsService, mCallback); inOrder.verify(mStatsService).forceUpdate(); - inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE); - inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE); - inOrder.verify(mNMService).untetherInterface(IFACE_NAME); + inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE); + inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE); + inOrder.verify(mNetd).tetherApplyDnsInterfaces(); + inOrder.verify(mNetd).tetherInterfaceRemove(IFACE_NAME); + inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME); inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName))); inOrder.verify(mCallback).updateInterfaceState( mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR); inOrder.verify(mCallback).updateLinkProperties( eq(mIpServer), any(LinkProperties.class)); - verifyNoMoreInteractions(mNMService, mStatsService, mCallback); + verifyNoMoreInteractions(mNetd, mStatsService, mCallback); } @Test @@ -361,13 +377,14 @@ public class IpServerTest { initTetheredStateMachine(TETHERING_USB, null); if (shouldThrow) { - doThrow(RemoteException.class).when(mNMService).untetherInterface(IFACE_NAME); + doThrow(RemoteException.class).when(mNetd).tetherInterfaceRemove(IFACE_NAME); } dispatchCommand(IpServer.CMD_INTERFACE_DOWN); - InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mCallback); - usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown(); - usbTeardownOrder.verify(mNMService).setInterfaceConfig( - IFACE_NAME, mInterfaceConfiguration); + InOrder usbTeardownOrder = inOrder(mNetd, mCallback); + // Currently IpServer interfaceSetCfg twice to stop IPv4. One just set interface down + // Another one is set IPv4 to 0.0.0.0/0 as clearng ipv4 address. + usbTeardownOrder.verify(mNetd, times(2)).interfaceSetCfg( + argThat(cfg -> IFACE_NAME.equals(cfg.ifName))); usbTeardownOrder.verify(mCallback).updateInterfaceState( mIpServer, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR); usbTeardownOrder.verify(mCallback).updateLinkProperties( @@ -380,12 +397,15 @@ public class IpServerTest { public void usbShouldBeTornDownOnTetherError() throws Exception { initStateMachine(TETHERING_USB); - doThrow(RemoteException.class).when(mNMService).tetherInterface(IFACE_NAME); + doThrow(RemoteException.class).when(mNetd).tetherInterfaceAdd(IFACE_NAME); dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED); - InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mCallback); - usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown(); - usbTeardownOrder.verify(mNMService).setInterfaceConfig( - IFACE_NAME, mInterfaceConfiguration); + InOrder usbTeardownOrder = inOrder(mNetd, mCallback); + usbTeardownOrder.verify(mNetd).interfaceSetCfg( + argThat(cfg -> IFACE_NAME.equals(cfg.ifName))); + usbTeardownOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME); + + usbTeardownOrder.verify(mNetd, times(2)).interfaceSetCfg( + argThat(cfg -> IFACE_NAME.equals(cfg.ifName))); usbTeardownOrder.verify(mCallback).updateInterfaceState( mIpServer, STATE_AVAILABLE, TETHER_ERROR_TETHER_IFACE_ERROR); usbTeardownOrder.verify(mCallback).updateLinkProperties( @@ -397,11 +417,13 @@ public class IpServerTest { public void shouldTearDownUsbOnUpstreamError() throws Exception { initTetheredStateMachine(TETHERING_USB, null); - doThrow(RemoteException.class).when(mNMService).enableNat(anyString(), anyString()); + doThrow(RemoteException.class).when(mNetd).tetherAddForward(anyString(), anyString()); dispatchTetherConnectionChanged(UPSTREAM_IFACE); - InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mCallback); - usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown(); - usbTeardownOrder.verify(mNMService).setInterfaceConfig(IFACE_NAME, mInterfaceConfiguration); + InOrder usbTeardownOrder = inOrder(mNetd, mCallback); + usbTeardownOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE); + + usbTeardownOrder.verify(mNetd, times(2)).interfaceSetCfg( + argThat(cfg -> IFACE_NAME.equals(cfg.ifName))); usbTeardownOrder.verify(mCallback).updateInterfaceState( mIpServer, STATE_AVAILABLE, TETHER_ERROR_ENABLE_NAT_ERROR); usbTeardownOrder.verify(mCallback).updateLinkProperties( @@ -413,11 +435,11 @@ public class IpServerTest { public void ignoresDuplicateUpstreamNotifications() throws Exception { initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE); - verifyNoMoreInteractions(mNMService, mStatsService, mCallback); + verifyNoMoreInteractions(mNetd, mStatsService, mCallback); for (int i = 0; i < 5; i++) { dispatchTetherConnectionChanged(UPSTREAM_IFACE); - verifyNoMoreInteractions(mNMService, mStatsService, mCallback); + verifyNoMoreInteractions(mNetd, mStatsService, mCallback); } } @@ -525,4 +547,12 @@ public class IpServerTest { // never see an empty interface name in any LinkProperties update. assertFalse(TextUtils.isEmpty(lp.getInterfaceName())); } + + private boolean assertContainsFlag(String[] flags, String match) { + for (String flag : flags) { + if (flag.equals(match)) return true; + } + fail("Missing flag: " + match); + return false; + } } diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java index 04b2eb411c..2f5eee4ed1 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java @@ -50,7 +50,6 @@ import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.any; -import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -74,7 +73,7 @@ import android.net.INetworkPolicyManager; import android.net.INetworkStatsService; import android.net.ITetheringEventCallback; import android.net.InetAddresses; -import android.net.InterfaceConfiguration; +import android.net.InterfaceConfigurationParcel; import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties; @@ -147,6 +146,7 @@ public class TetheringTest { private static final String TEST_USB_IFNAME = "test_rndis0"; private static final String TEST_WLAN_IFNAME = "test_wlan0"; private static final String TEST_P2P_IFNAME = "test_p2p-p2p0-0"; + private static final String TETHERING_NAME = "Tethering"; private static final int DHCPSERVER_START_TIMEOUT_MS = 1000; @@ -185,6 +185,7 @@ public class TetheringTest { private BroadcastReceiver mBroadcastReceiver; private Tethering mTethering; private PhoneStateListener mPhoneStateListener; + private InterfaceConfigurationParcel mInterfaceConfiguration; private class TestContext extends BroadcastInterceptingContext { TestContext(Context base) { @@ -247,11 +248,6 @@ public class TetheringTest { MacAddress.ALL_ZEROS_ADDRESS); } - @Override - public INetd getNetdService() { - return mNetd; - } - @Override public void makeDhcpServer(String ifName, DhcpServingParamsParcel params, DhcpServerCallbacks cb) { @@ -429,11 +425,11 @@ public class TetheringTest { .thenReturn(new int[0]); when(mResources.getBoolean(com.android.internal.R.bool.config_tether_upstream_automatic)) .thenReturn(false); - when(mNMService.listInterfaces()) + when(mNetd.interfaceGetList()) .thenReturn(new String[] { TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME}); - when(mNMService.getInterfaceConfig(anyString())) - .thenReturn(new InterfaceConfiguration()); + mInterfaceConfiguration = new InterfaceConfigurationParcel(); + mInterfaceConfiguration.flags = new String[0]; when(mRouterAdvertisementDaemon.start()) .thenReturn(true); @@ -523,10 +519,11 @@ public class TetheringTest { } private void verifyInterfaceServingModeStarted(String ifname) throws Exception { - verify(mNMService, times(1)).getInterfaceConfig(ifname); - verify(mNMService, times(1)) - .setInterfaceConfig(eq(ifname), any(InterfaceConfiguration.class)); - verify(mNMService, times(1)).tetherInterface(ifname); + verify(mNetd, times(1)).interfaceSetCfg(any(InterfaceConfigurationParcel.class)); + verify(mNetd, times(1)).tetherInterfaceAdd(ifname); + verify(mNetd, times(1)).networkAddInterface(INetd.LOCAL_NET_ID, ifname); + verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(ifname), + anyString(), anyString()); } private void verifyTetheringBroadcast(String ifname, String whichExtra) { @@ -558,7 +555,7 @@ public class TetheringTest { verify(mWifiManager).updateInterfaceIpState( TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED); } - verifyNoMoreInteractions(mNMService); + verifyNoMoreInteractions(mNetd); verifyNoMoreInteractions(mWifiManager); } @@ -581,14 +578,14 @@ public class TetheringTest { prepareUsbTethering(upstreamState); // This should produce no activity of any kind. - verifyNoMoreInteractions(mNMService); + verifyNoMoreInteractions(mNetd); // Pretend we then receive USB configured broadcast. sendUsbBroadcast(true, true, true); mLooper.dispatchAll(); // Now we should see the start of tethering mechanics (in this case: // tetherMatchingInterfaces() which starts by fetching all interfaces). - verify(mNMService, times(1)).listInterfaces(); + verify(mNetd, times(1)).interfaceGetList(); // UpstreamNetworkMonitor should receive selected upstream verify(mUpstreamNetworkMonitor, times(1)).selectPreferredUpstreamType(any()); @@ -618,9 +615,9 @@ public class TetheringTest { verifyInterfaceServingModeStarted(TEST_WLAN_IFNAME); verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER); - verify(mNMService, times(1)).setIpForwardingEnabled(true); - verify(mNMService, times(1)).startTethering(any(String[].class)); - verifyNoMoreInteractions(mNMService); + verify(mNetd, times(1)).ipfwdEnableForwarding(TETHERING_NAME); + verify(mNetd, times(1)).tetherStartWithConfiguration(any()); + verifyNoMoreInteractions(mNetd); verify(mWifiManager).updateInterfaceIpState( TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED); verify(mWifiManager).updateInterfaceIpState( @@ -638,16 +635,16 @@ public class TetheringTest { mTethering.interfaceRemoved(TEST_WLAN_IFNAME); mLooper.dispatchAll(); - verify(mNMService, times(1)).untetherInterface(TEST_WLAN_IFNAME); - // {g,s}etInterfaceConfig() called twice for enabling and disabling IPv4. - verify(mNMService, times(2)).getInterfaceConfig(TEST_WLAN_IFNAME); - verify(mNMService, times(2)) - .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class)); - verify(mNMService, times(1)).stopTethering(); - verify(mNMService, times(1)).setIpForwardingEnabled(false); + verify(mNetd, times(1)).tetherApplyDnsInterfaces(); + verify(mNetd, times(1)).tetherInterfaceRemove(TEST_WLAN_IFNAME); + verify(mNetd, times(1)).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_WLAN_IFNAME); + // interfaceSetCfg() called once for enabling and twice disabling IPv4. + verify(mNetd, times(3)).interfaceSetCfg(any(InterfaceConfigurationParcel.class)); + verify(mNetd, times(1)).tetherStop(); + verify(mNetd, times(1)).ipfwdDisableForwarding(TETHERING_NAME); verify(mWifiManager, times(3)).updateInterfaceIpState( TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED); - verifyNoMoreInteractions(mNMService); + verifyNoMoreInteractions(mNetd); verifyNoMoreInteractions(mWifiManager); // Asking for the last error after the per-interface state machine // has been reaped yields an unknown interface error. @@ -684,8 +681,8 @@ public class TetheringTest { UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState(); runUsbTethering(upstreamState); - verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); - verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); sendIPv6TetherUpdates(upstreamState); verify(mRouterAdvertisementDaemon, never()).buildNewRa(any(), notNull()); @@ -708,8 +705,8 @@ public class TetheringTest { UpstreamNetworkState upstreamState = buildMobileIPv6UpstreamState(); runUsbTethering(upstreamState); - verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); - verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); sendIPv6TetherUpdates(upstreamState); verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull()); @@ -721,8 +718,8 @@ public class TetheringTest { UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState(); runUsbTethering(upstreamState); - verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); - verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); verify(mRouterAdvertisementDaemon, times(1)).start(); verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any()); @@ -736,12 +733,11 @@ public class TetheringTest { UpstreamNetworkState upstreamState = buildMobile464xlatUpstreamState(); runUsbTethering(upstreamState); - verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME); - verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME); + verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any()); - verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); - verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, - TEST_XLAT_MOBILE_IFNAME); + verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME); + verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); sendIPv6TetherUpdates(upstreamState); verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull()); @@ -754,9 +750,9 @@ public class TetheringTest { UpstreamNetworkState upstreamState = buildMobileIPv6UpstreamState(); runUsbTethering(upstreamState); - verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any()); - verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); // Then 464xlat comes up upstreamState = buildMobile464xlatUpstreamState(); @@ -772,12 +768,11 @@ public class TetheringTest { mLooper.dispatchAll(); // Forwarding is added for 464xlat - verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME); - verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, - TEST_XLAT_MOBILE_IFNAME); + verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME); + verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME); // Forwarding was not re-added for v6 (still times(1)) - verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); - verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); // DHCP not restarted on downstream (still times(1)) verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any()); } @@ -820,7 +815,7 @@ public class TetheringTest { mLooper.dispatchAll(); verify(mWifiManager, times(1)).startTetheredHotspot(null); verifyNoMoreInteractions(mWifiManager); - verifyNoMoreInteractions(mNMService); + verifyNoMoreInteractions(mNetd); // Emulate externally-visible WifiManager effects, causing the // per-interface state machine to start up, and telling us that @@ -833,7 +828,7 @@ public class TetheringTest { verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER); verify(mWifiManager).updateInterfaceIpState( TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED); - verifyNoMoreInteractions(mNMService); + verifyNoMoreInteractions(mNetd); verifyNoMoreInteractions(mWifiManager); } @@ -847,7 +842,7 @@ public class TetheringTest { mLooper.dispatchAll(); verify(mWifiManager, times(1)).startTetheredHotspot(null); verifyNoMoreInteractions(mWifiManager); - verifyNoMoreInteractions(mNMService); + verifyNoMoreInteractions(mNetd); // Emulate externally-visible WifiManager effects, causing the // per-interface state machine to start up, and telling us that @@ -858,9 +853,11 @@ public class TetheringTest { verifyInterfaceServingModeStarted(TEST_WLAN_IFNAME); verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER); - verify(mNMService, times(1)).setIpForwardingEnabled(true); - verify(mNMService, times(1)).startTethering(any(String[].class)); - verifyNoMoreInteractions(mNMService); + verify(mNetd, times(1)).ipfwdEnableForwarding(TETHERING_NAME); + verify(mNetd, times(1)).tetherStartWithConfiguration(any()); + verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(TEST_WLAN_IFNAME), + anyString(), anyString()); + verifyNoMoreInteractions(mNetd); verify(mWifiManager).updateInterfaceIpState( TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED); verify(mWifiManager).updateInterfaceIpState( @@ -878,8 +875,8 @@ public class TetheringTest { ///// // We do not currently emulate any upstream being found. // - // This is why there are no calls to verify mNMService.enableNat() or - // mNMService.startInterfaceForwarding(). + // This is why there are no calls to verify mNetd.tetherAddForward() or + // mNetd.ipfwdAddInterfaceForward(). ///// // Emulate pressing the WiFi tethering button. @@ -887,7 +884,7 @@ public class TetheringTest { mLooper.dispatchAll(); verify(mWifiManager, times(1)).stopSoftAp(); verifyNoMoreInteractions(mWifiManager); - verifyNoMoreInteractions(mNMService); + verifyNoMoreInteractions(mNetd); // Emulate externally-visible WifiManager effects, when tethering mode // is being torn down. @@ -895,16 +892,16 @@ public class TetheringTest { mTethering.interfaceRemoved(TEST_WLAN_IFNAME); mLooper.dispatchAll(); - verify(mNMService, times(1)).untetherInterface(TEST_WLAN_IFNAME); - // {g,s}etInterfaceConfig() called twice for enabling and disabling IPv4. - verify(mNMService, atLeastOnce()).getInterfaceConfig(TEST_WLAN_IFNAME); - verify(mNMService, atLeastOnce()) - .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class)); - verify(mNMService, times(1)).stopTethering(); - verify(mNMService, times(1)).setIpForwardingEnabled(false); + verify(mNetd, times(1)).tetherApplyDnsInterfaces(); + verify(mNetd, times(1)).tetherInterfaceRemove(TEST_WLAN_IFNAME); + verify(mNetd, times(1)).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_WLAN_IFNAME); + // interfaceSetCfg() called once for enabling and twice for disabling IPv4. + verify(mNetd, times(3)).interfaceSetCfg(any(InterfaceConfigurationParcel.class)); + verify(mNetd, times(1)).tetherStop(); + verify(mNetd, times(1)).ipfwdDisableForwarding(TETHERING_NAME); verify(mWifiManager, times(3)).updateInterfaceIpState( TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED); - verifyNoMoreInteractions(mNMService); + verifyNoMoreInteractions(mNetd); verifyNoMoreInteractions(mWifiManager); // Asking for the last error after the per-interface state machine // has been reaped yields an unknown interface error. @@ -915,14 +912,14 @@ public class TetheringTest { @Test public void failureEnablingIpForwarding() throws Exception { when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true); - doThrow(new RemoteException()).when(mNMService).setIpForwardingEnabled(true); + doThrow(new RemoteException()).when(mNetd).ipfwdEnableForwarding(TETHERING_NAME); // Emulate pressing the WiFi tethering button. mTethering.startTethering(TETHERING_WIFI, null, false); mLooper.dispatchAll(); verify(mWifiManager, times(1)).startTetheredHotspot(null); verifyNoMoreInteractions(mWifiManager); - verifyNoMoreInteractions(mNMService); + verifyNoMoreInteractions(mNetd); // Emulate externally-visible WifiManager effects, causing the // per-interface state machine to start up, and telling us that @@ -931,15 +928,15 @@ public class TetheringTest { sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED); mLooper.dispatchAll(); - // We verify get/set called thrice here: twice for setup (on NMService) and once during - // teardown (on Netd) because all events happen over the course of the single + // We verify get/set called three times here: twice for setup and once during + // teardown because all events happen over the course of the single // dispatchAll() above. Note that once the IpServer IPv4 address config // code is refactored the two calls during shutdown will revert to one. - verify(mNMService, times(2)).getInterfaceConfig(TEST_WLAN_IFNAME); - verify(mNMService, times(2)) - .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class)); - verify(mNetd, times(1)).interfaceSetCfg(argThat(p -> TEST_WLAN_IFNAME.equals(p.ifName))); - verify(mNMService, times(1)).tetherInterface(TEST_WLAN_IFNAME); + verify(mNetd, times(3)).interfaceSetCfg(argThat(p -> TEST_WLAN_IFNAME.equals(p.ifName))); + verify(mNetd, times(1)).tetherInterfaceAdd(TEST_WLAN_IFNAME); + verify(mNetd, times(1)).networkAddInterface(INetd.LOCAL_NET_ID, TEST_WLAN_IFNAME); + verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(TEST_WLAN_IFNAME), + anyString(), anyString()); verify(mWifiManager).updateInterfaceIpState( TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED); verify(mWifiManager).updateInterfaceIpState( @@ -949,18 +946,20 @@ public class TetheringTest { assertEquals(3, mTetheringDependencies.mIsTetheringSupportedCalls); verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER); // This is called, but will throw. - verify(mNMService, times(1)).setIpForwardingEnabled(true); + verify(mNetd, times(1)).ipfwdEnableForwarding(TETHERING_NAME); // This never gets called because of the exception thrown above. - verify(mNMService, times(0)).startTethering(any(String[].class)); + verify(mNetd, times(0)).tetherStartWithConfiguration(any()); // When the master state machine transitions to an error state it tells // downstream interfaces, which causes us to tell Wi-Fi about the error // so it can take down AP mode. - verify(mNMService, times(1)).untetherInterface(TEST_WLAN_IFNAME); + verify(mNetd, times(1)).tetherApplyDnsInterfaces(); + verify(mNetd, times(1)).tetherInterfaceRemove(TEST_WLAN_IFNAME); + verify(mNetd, times(1)).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_WLAN_IFNAME); verify(mWifiManager).updateInterfaceIpState( TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR); verifyNoMoreInteractions(mWifiManager); - verifyNoMoreInteractions(mNMService); + verifyNoMoreInteractions(mNetd); } private void runUserRestrictionsChange( @@ -1232,9 +1231,9 @@ public class TetheringTest { verifyInterfaceServingModeStarted(TEST_P2P_IFNAME); verifyTetheringBroadcast(TEST_P2P_IFNAME, EXTRA_AVAILABLE_TETHER); - verify(mNMService, times(1)).setIpForwardingEnabled(true); - verify(mNMService, times(1)).startTethering(any(String[].class)); - verifyNoMoreInteractions(mNMService); + verify(mNetd, times(1)).ipfwdEnableForwarding(TETHERING_NAME); + verify(mNetd, times(1)).tetherStartWithConfiguration(any()); + verifyNoMoreInteractions(mNetd); verifyTetheringBroadcast(TEST_P2P_IFNAME, EXTRA_ACTIVE_LOCAL_ONLY); verify(mUpstreamNetworkMonitor, times(1)).startObserveAllNetworks(); // This will be called twice, one is on entering IpServer.STATE_AVAILABLE, @@ -1249,16 +1248,16 @@ public class TetheringTest { mTethering.interfaceRemoved(TEST_P2P_IFNAME); mLooper.dispatchAll(); - verify(mNMService, times(1)).untetherInterface(TEST_P2P_IFNAME); - // {g,s}etInterfaceConfig() called twice for enabling and disabling IPv4. - verify(mNMService, times(2)).getInterfaceConfig(TEST_P2P_IFNAME); - verify(mNMService, times(2)) - .setInterfaceConfig(eq(TEST_P2P_IFNAME), any(InterfaceConfiguration.class)); - verify(mNMService, times(1)).stopTethering(); - verify(mNMService, times(1)).setIpForwardingEnabled(false); + verify(mNetd, times(1)).tetherApplyDnsInterfaces(); + verify(mNetd, times(1)).tetherInterfaceRemove(TEST_P2P_IFNAME); + verify(mNetd, times(1)).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_P2P_IFNAME); + // interfaceSetCfg() called once for enabling and twice for disabling IPv4. + verify(mNetd, times(3)).interfaceSetCfg(any(InterfaceConfigurationParcel.class)); + verify(mNetd, times(1)).tetherStop(); + verify(mNetd, times(1)).ipfwdDisableForwarding(TETHERING_NAME); verify(mUpstreamNetworkMonitor, never()).getCurrentPreferredUpstream(); verify(mUpstreamNetworkMonitor, never()).selectPreferredUpstreamType(any()); - verifyNoMoreInteractions(mNMService); + verifyNoMoreInteractions(mNetd); // Asking for the last error after the per-interface state machine // has been reaped yields an unknown interface error. assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_P2P_IFNAME)); @@ -1272,12 +1271,11 @@ public class TetheringTest { sendWifiP2pConnectionChanged(true, false, TEST_P2P_IFNAME); mLooper.dispatchAll(); - verify(mNMService, never()).getInterfaceConfig(TEST_P2P_IFNAME); - verify(mNMService, never()) - .setInterfaceConfig(eq(TEST_P2P_IFNAME), any(InterfaceConfiguration.class)); - verify(mNMService, never()).tetherInterface(TEST_P2P_IFNAME); - verify(mNMService, never()).setIpForwardingEnabled(true); - verify(mNMService, never()).startTethering(any(String[].class)); + verify(mNetd, never()).interfaceSetCfg(any(InterfaceConfigurationParcel.class)); + verify(mNetd, never()).tetherInterfaceAdd(TEST_P2P_IFNAME); + verify(mNetd, never()).networkAddInterface(INetd.LOCAL_NET_ID, TEST_P2P_IFNAME); + verify(mNetd, never()).ipfwdEnableForwarding(TETHERING_NAME); + verify(mNetd, never()).tetherStartWithConfiguration(any()); // Emulate externally-visible WifiP2pManager effects, when wifi p2p group // is being removed. @@ -1285,13 +1283,13 @@ public class TetheringTest { mTethering.interfaceRemoved(TEST_P2P_IFNAME); mLooper.dispatchAll(); - verify(mNMService, never()).untetherInterface(TEST_P2P_IFNAME); - verify(mNMService, never()).getInterfaceConfig(TEST_P2P_IFNAME); - verify(mNMService, never()) - .setInterfaceConfig(eq(TEST_P2P_IFNAME), any(InterfaceConfiguration.class)); - verify(mNMService, never()).stopTethering(); - verify(mNMService, never()).setIpForwardingEnabled(false); - verifyNoMoreInteractions(mNMService); + verify(mNetd, never()).tetherApplyDnsInterfaces(); + verify(mNetd, never()).tetherInterfaceRemove(TEST_P2P_IFNAME); + verify(mNetd, never()).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_P2P_IFNAME); + verify(mNetd, never()).interfaceSetCfg(any(InterfaceConfigurationParcel.class)); + verify(mNetd, never()).tetherStop(); + verify(mNetd, never()).ipfwdDisableForwarding(TETHERING_NAME); + verifyNoMoreInteractions(mNetd); // Asking for the last error after the per-interface state machine // has been reaped yields an unknown interface error. assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_P2P_IFNAME)); @@ -1321,12 +1319,11 @@ public class TetheringTest { sendWifiP2pConnectionChanged(true, true, TEST_P2P_IFNAME); mLooper.dispatchAll(); - verify(mNMService, never()).getInterfaceConfig(TEST_P2P_IFNAME); - verify(mNMService, never()) - .setInterfaceConfig(eq(TEST_P2P_IFNAME), any(InterfaceConfiguration.class)); - verify(mNMService, never()).tetherInterface(TEST_P2P_IFNAME); - verify(mNMService, never()).setIpForwardingEnabled(true); - verify(mNMService, never()).startTethering(any(String[].class)); + verify(mNetd, never()).interfaceSetCfg(any(InterfaceConfigurationParcel.class)); + verify(mNetd, never()).tetherInterfaceAdd(TEST_P2P_IFNAME); + verify(mNetd, never()).networkAddInterface(INetd.LOCAL_NET_ID, TEST_P2P_IFNAME); + verify(mNetd, never()).ipfwdEnableForwarding(TETHERING_NAME); + verify(mNetd, never()).tetherStartWithConfiguration(any()); assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_P2P_IFNAME)); } @Test From 489e09abae2755215718e36edbdc334df11131c9 Mon Sep 17 00:00:00 2001 From: Jiyong Park Date: Mon, 6 Jan 2020 13:30:59 +0900 Subject: [PATCH 015/188] Platform shouldn't directly link to jars in APEXes The non-updatable part of the platform shouldn't directly link to the boot jars in APEXes. Ensure this by 1) setting the visibility property for the boot jars so that they are not visible to non-APEX modules and 2) setting the apex_available property so that the boot jars are only built for the corresponding APEXes, but not for others. Bug: b/146167933 Bug: b/146218515 Bug: b/147200698 Test: m Change-Id: I251fabd773bc31f46d572d143c72dd9162f3f0a6 --- Tethering/Android.bp | 1 + Tethering/common/TetheringLib/Android.bp | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/Tethering/Android.bp b/Tethering/Android.bp index 0be853a81f..2061561d60 100644 --- a/Tethering/Android.bp +++ b/Tethering/Android.bp @@ -123,4 +123,5 @@ android_app { use_embedded_native_libs: true, // The permission configuration *must* be included to ensure security of the device required: ["NetworkPermissionConfig"], + apex_available: ["com.android.tethering"], } diff --git a/Tethering/common/TetheringLib/Android.bp b/Tethering/common/TetheringLib/Android.bp index 5785707cb9..264ce440f5 100644 --- a/Tethering/common/TetheringLib/Android.bp +++ b/Tethering/common/TetheringLib/Android.bp @@ -47,6 +47,16 @@ java_library { libs: [ "android_system_stubs_current", ], + + hostdex: true, // for hiddenapi check + visibility: [ + "//frameworks/base/packages/Tethering:__subpackages__", + //TODO(b/147200698) remove below lines when the platform is built with stubs + "//frameworks/base", + "//frameworks/base/services", + "//frameworks/base/services/core", + ], + apex_available: ["com.android.tethering"], } filegroup { From 5479a4e669b35e25cd60fe2efcf36e702bae3afe Mon Sep 17 00:00:00 2001 From: David Su Date: Mon, 13 Jan 2020 12:49:21 -0800 Subject: [PATCH 016/188] TetheringTests: Stop using @hide Wifi P2P APIs Instead use Mockito to achieve the same thing. Bug: 138801922 Test: atest TetheringTests Change-Id: I41edd697017f4ce59e6707302bebc0da3d75831d --- .../connectivity/tethering/TetheringTest.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java index 7af48a89d8..6edb431aab 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java @@ -489,13 +489,15 @@ public class TetheringTest { p2pInfo.groupFormed = isGroupFormed; p2pInfo.isGroupOwner = isGroupOwner; - WifiP2pGroup group = new WifiP2pGroup(); - group.setIsGroupOwner(isGroupOwner); - group.setInterface(ifname); + WifiP2pGroup group = mock(WifiP2pGroup.class); + when(group.isGroupOwner()).thenReturn(isGroupOwner); + when(group.getInterface()).thenReturn(ifname); + + final Intent intent = mock(Intent.class); + when(intent.getAction()).thenReturn(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION); + when(intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO)).thenReturn(p2pInfo); + when(intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP)).thenReturn(group); - final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION); - intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO, p2pInfo); - intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP, group); mServiceContext.sendBroadcastAsUserMultiplePermissions(intent, UserHandle.ALL, P2P_RECEIVER_PERMISSIONS_FOR_BROADCAST); } From 1a4b23585a5d7ee5b2d8adb84708e2f68beef61d Mon Sep 17 00:00:00 2001 From: markchien Date: Wed, 15 Jan 2020 14:25:32 +0800 Subject: [PATCH 017/188] Tethering: add p2p regex for tethering modes Bug: 147399354 Test: build Change-Id: Iec666cc302ee749ddb57379f42f1e540ed813e48 --- Tethering/res/values/config.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Tethering/res/values/config.xml b/Tethering/res/values/config.xml index ca290c6377..d61f8e19ac 100644 --- a/Tethering/res/values/config.xml +++ b/Tethering/res/values/config.xml @@ -38,8 +38,9 @@ + should be empty. An example would be "p2p-p2p\\d-.*" --> + "p2p-p2p\\d-.*" + + + diff --git a/Tethering/res/values/overlayable.xml b/Tethering/res/values/overlayable.xml index e089d9d199..fe025c7ac9 100644 --- a/Tethering/res/values/overlayable.xml +++ b/Tethering/res/values/overlayable.xml @@ -17,6 +17,7 @@ + diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java index 4306cf0bd5..baee99a5a5 100644 --- a/Tethering/src/android/net/ip/IpServer.java +++ b/Tethering/src/android/net/ip/IpServer.java @@ -416,7 +416,8 @@ public class IpServer extends StateMachine { final Inet4Address srvAddr; int prefixLen = 0; try { - if (mInterfaceType == TetheringManager.TETHERING_USB) { + if (mInterfaceType == TetheringManager.TETHERING_USB + || mInterfaceType == TetheringManager.TETHERING_NCM) { srvAddr = (Inet4Address) parseNumericAddress(USB_NEAR_IFACE_ADDR); prefixLen = USB_PREFIX_LENGTH; } else if (mInterfaceType == TetheringManager.TETHERING_WIFI) { diff --git a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java index fdc045fb15..f4d083194d 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java +++ b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java @@ -19,6 +19,7 @@ package com.android.server.connectivity.tethering; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.hardware.usb.UsbManager.USB_CONFIGURED; import static android.hardware.usb.UsbManager.USB_CONNECTED; +import static android.hardware.usb.UsbManager.USB_FUNCTION_NCM; import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS; import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; @@ -30,6 +31,7 @@ import static android.net.TetheringManager.EXTRA_AVAILABLE_TETHER; import static android.net.TetheringManager.EXTRA_ERRORED_TETHER; import static android.net.TetheringManager.TETHERING_BLUETOOTH; import static android.net.TetheringManager.TETHERING_INVALID; +import static android.net.TetheringManager.TETHERING_NCM; import static android.net.TetheringManager.TETHERING_USB; import static android.net.TetheringManager.TETHERING_WIFI; import static android.net.TetheringManager.TETHERING_WIFI_P2P; @@ -407,6 +409,8 @@ public class Tethering { return TETHERING_USB; } else if (cfg.isBluetooth(iface)) { return TETHERING_BLUETOOTH; + } else if (cfg.isNcm(iface)) { + return TETHERING_NCM; } return TETHERING_INVALID; } @@ -455,6 +459,10 @@ public class Tethering { case TETHERING_BLUETOOTH: setBluetoothTethering(enable, listener); break; + case TETHERING_NCM: + result = setNcmTethering(enable); + sendTetherResult(listener, result); + break; default: Log.w(TAG, "Invalid tether type."); sendTetherResult(listener, TETHER_ERROR_UNKNOWN_IFACE); @@ -804,6 +812,7 @@ public class Tethering { final boolean usbConnected = intent.getBooleanExtra(USB_CONNECTED, false); final boolean usbConfigured = intent.getBooleanExtra(USB_CONFIGURED, false); final boolean rndisEnabled = intent.getBooleanExtra(USB_FUNCTION_RNDIS, false); + final boolean ncmEnabled = intent.getBooleanExtra(USB_FUNCTION_NCM, false); mLog.log(String.format("USB bcast connected:%s configured:%s rndis:%s", usbConnected, usbConfigured, rndisEnabled)); @@ -831,6 +840,8 @@ public class Tethering { } else if (usbConfigured && rndisEnabled) { // Tether if rndis is enabled and usb is configured. tetherMatchingInterfaces(IpServer.STATE_TETHERED, TETHERING_USB); + } else if (usbConnected && ncmEnabled) { + tetherMatchingInterfaces(IpServer.STATE_LOCAL_ONLY, TETHERING_NCM); } mRndisEnabled = usbConfigured && rndisEnabled; } @@ -1118,6 +1129,16 @@ public class Tethering { return TETHER_ERROR_NO_ERROR; } + private int setNcmTethering(boolean enable) { + if (VDBG) Log.d(TAG, "setNcmTethering(" + enable + ")"); + UsbManager usbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE); + synchronized (mPublicSync) { + usbManager.setCurrentFunctions(enable ? UsbManager.FUNCTION_NCM + : UsbManager.FUNCTION_NONE); + } + return TETHER_ERROR_NO_ERROR; + } + // TODO review API - figure out how to delete these entirely. String[] getTetheredIfaces() { ArrayList list = new ArrayList(); diff --git a/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java b/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java index 068c346fbf..7e9e26f5af 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java +++ b/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java @@ -83,6 +83,7 @@ public class TetheringConfiguration { public final String[] tetherableWifiRegexs; public final String[] tetherableWifiP2pRegexs; public final String[] tetherableBluetoothRegexs; + public final String[] tetherableNcmRegexs; public final boolean isDunRequired; public final boolean chooseUpstreamAutomatically; public final Collection preferredUpstreamIfaceTypes; @@ -103,6 +104,7 @@ public class TetheringConfiguration { Resources res = getResources(ctx, activeDataSubId); tetherableUsbRegexs = getResourceStringArray(res, R.array.config_tether_usb_regexs); + tetherableNcmRegexs = getResourceStringArray(res, R.array.config_tether_ncm_regexs); // TODO: Evaluate deleting this altogether now that Wi-Fi always passes // us an interface name. Careful consideration needs to be given to // implications for Settings and for provisioning checks. @@ -156,6 +158,11 @@ public class TetheringConfiguration { return matchesDownstreamRegexs(iface, tetherableBluetoothRegexs); } + /** Check if interface is ncm */ + public boolean isNcm(String iface) { + return matchesDownstreamRegexs(iface, tetherableNcmRegexs); + } + /** Check whether no ui entitlement application is available.*/ public boolean hasMobileHotspotProvisionApp() { return !TextUtils.isEmpty(provisioningAppNoUi); @@ -170,6 +177,7 @@ public class TetheringConfiguration { dumpStringArray(pw, "tetherableWifiRegexs", tetherableWifiRegexs); dumpStringArray(pw, "tetherableWifiP2pRegexs", tetherableWifiP2pRegexs); dumpStringArray(pw, "tetherableBluetoothRegexs", tetherableBluetoothRegexs); + dumpStringArray(pw, "tetherableNcmRegexs", tetherableNcmRegexs); pw.print("isDunRequired: "); pw.println(isDunRequired); diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java index 932fd50622..8440562aae 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java @@ -18,6 +18,7 @@ package com.android.server.connectivity.tethering; import static android.hardware.usb.UsbManager.USB_CONFIGURED; import static android.hardware.usb.UsbManager.USB_CONNECTED; +import static android.hardware.usb.UsbManager.USB_FUNCTION_NCM; import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS; import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED; import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED; @@ -27,6 +28,7 @@ import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED; import static android.net.TetheringManager.EXTRA_ACTIVE_LOCAL_ONLY; import static android.net.TetheringManager.EXTRA_ACTIVE_TETHER; import static android.net.TetheringManager.EXTRA_AVAILABLE_TETHER; +import static android.net.TetheringManager.TETHERING_NCM; import static android.net.TetheringManager.TETHERING_USB; import static android.net.TetheringManager.TETHERING_WIFI; import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; @@ -151,6 +153,7 @@ public class TetheringTest { private static final String TEST_USB_IFNAME = "test_rndis0"; private static final String TEST_WLAN_IFNAME = "test_wlan0"; private static final String TEST_P2P_IFNAME = "test_p2p-p2p0-0"; + private static final String TEST_NCM_IFNAME = "test_ncm0"; private static final String TETHERING_NAME = "Tethering"; private static final int DHCPSERVER_START_TIMEOUT_MS = 1000; @@ -252,9 +255,11 @@ public class TetheringTest { ifName.equals(TEST_USB_IFNAME) || ifName.equals(TEST_WLAN_IFNAME) || ifName.equals(TEST_MOBILE_IFNAME) - || ifName.equals(TEST_P2P_IFNAME)); + || ifName.equals(TEST_P2P_IFNAME) + || ifName.equals(TEST_NCM_IFNAME)); final String[] ifaces = new String[] { - TEST_USB_IFNAME, TEST_WLAN_IFNAME, TEST_MOBILE_IFNAME, TEST_P2P_IFNAME}; + TEST_USB_IFNAME, TEST_WLAN_IFNAME, TEST_MOBILE_IFNAME, TEST_P2P_IFNAME, + TEST_NCM_IFNAME}; return new InterfaceParams(ifName, ArrayUtils.indexOf(ifaces, ifName) + IFINDEX_OFFSET, MacAddress.ALL_ZEROS_ADDRESS); } @@ -428,13 +433,16 @@ public class TetheringTest { .thenReturn(new String[]{ "test_p2p-p2p\\d-.*" }); when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs)) .thenReturn(new String[0]); + when(mResources.getStringArray(R.array.config_tether_ncm_regexs)) + .thenReturn(new String[] { "test_ncm\\d" }); when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[0]); when(mResources.getBoolean(R.bool.config_tether_upstream_automatic)).thenReturn(false); when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( false); when(mNetd.interfaceGetList()) .thenReturn(new String[] { - TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME}); + TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME, + TEST_NCM_IFNAME}); when(mResources.getString(R.string.config_wifi_tether_enable)).thenReturn(""); mInterfaceConfiguration = new InterfaceConfigurationParcel(); mInterfaceConfiguration.flags = new String[0]; @@ -522,11 +530,16 @@ public class TetheringTest { P2P_RECEIVER_PERMISSIONS_FOR_BROADCAST); } - private void sendUsbBroadcast(boolean connected, boolean configured, boolean rndisFunction) { + private void sendUsbBroadcast(boolean connected, boolean configured, boolean function, + int type) { final Intent intent = new Intent(UsbManager.ACTION_USB_STATE); intent.putExtra(USB_CONNECTED, connected); intent.putExtra(USB_CONFIGURED, configured); - intent.putExtra(USB_FUNCTION_RNDIS, rndisFunction); + if (type == TETHERING_USB) { + intent.putExtra(USB_FUNCTION_RNDIS, function); + } else { + intent.putExtra(USB_FUNCTION_NCM, function); + } mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); } @@ -576,6 +589,15 @@ public class TetheringTest { verifyNoMoreInteractions(mWifiManager); } + private void prepareNcmTethering() { + // Emulate startTethering(TETHERING_NCM) called + mTethering.startTethering(createTetheringRquestParcel(TETHERING_NCM), null); + mLooper.dispatchAll(); + verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NCM); + + mTethering.interfaceStatusChanged(TEST_NCM_IFNAME, true); + } + private void prepareUsbTethering(UpstreamNetworkState upstreamState) { when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState); when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())) @@ -598,7 +620,7 @@ public class TetheringTest { verifyNoMoreInteractions(mNetd); // Pretend we then receive USB configured broadcast. - sendUsbBroadcast(true, true, true); + sendUsbBroadcast(true, true, true, TETHERING_USB); mLooper.dispatchAll(); // Now we should see the start of tethering mechanics (in this case: // tetherMatchingInterfaces() which starts by fetching all interfaces). @@ -689,7 +711,7 @@ public class TetheringTest { private void runUsbTethering(UpstreamNetworkState upstreamState) { prepareUsbTethering(upstreamState); - sendUsbBroadcast(true, true, true); + sendUsbBroadcast(true, true, true, TETHERING_USB); mLooper.dispatchAll(); } @@ -812,6 +834,29 @@ public class TetheringTest { verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(upstreamState.network); } + private void runNcmTethering() { + prepareNcmTethering(); + sendUsbBroadcast(true, true, true, TETHERING_NCM); + mLooper.dispatchAll(); + } + + @Test + public void workingNcmTethering() throws Exception { + runNcmTethering(); + + verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any()); + } + + @Test + public void workingNcmTethering_LegacyDhcp() { + when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( + true); + sendConfigurationChanged(); + runNcmTethering(); + + verify(mIpServerDependencies, never()).makeDhcpServer(any(), any(), any()); + } + @Test public void workingLocalOnlyHotspotEnrichedApBroadcastWithIfaceChanged() throws Exception { workingLocalOnlyHotspotEnrichedApBroadcast(true); From bfc45e8b977bee3c608be15fb9885e5f05228c23 Mon Sep 17 00:00:00 2001 From: Baligh Uddin Date: Sat, 4 Jan 2020 15:49:29 -0800 Subject: [PATCH 026/188] Remove hardcoded min, targetSDK for 'R' version of modules BUG: 130541924 Change-Id: Idba436b7983c92e5a4f727e32456552bccb3990f Exempt-From-Owner-Approval: baligh@ approving to unblock TM --- Tethering/apex/AndroidManifest.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Tethering/apex/AndroidManifest.xml b/Tethering/apex/AndroidManifest.xml index 5c35c51dc7..4aae3cc300 100644 --- a/Tethering/apex/AndroidManifest.xml +++ b/Tethering/apex/AndroidManifest.xml @@ -20,8 +20,10 @@ - + From 5c14666b09183369b69a6a495f35a71f8b5b01cb Mon Sep 17 00:00:00 2001 From: Remi NGUYEN VAN Date: Fri, 24 Jan 2020 22:57:09 +0900 Subject: [PATCH 027/188] Add support for Ethernet tethering Ethernet tethering can be started via startTethering(TETHERING_ETHERNET). Test: flashed, enabled ethernet tethering, verified internet access on downstream. Bug: 130840861 Change-Id: I34842acd94b972e440c3622f7617df10c18acf65 --- .../src/android/net/TetheringManager.java | 6 ++ Tethering/src/android/net/ip/IpServer.java | 6 ++ .../connectivity/tethering/Tethering.java | 66 +++++++++++++++++++ Tethering/tests/unit/Android.bp | 1 + Tethering/tests/unit/AndroidManifest.xml | 2 + 5 files changed, 81 insertions(+) diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java index 79c6930404..37ce1d57da 100644 --- a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java +++ b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java @@ -136,6 +136,12 @@ public class TetheringManager { */ public static final int TETHERING_NCM = 4; + /** + * Ethernet tethering type. + * @see #startTethering(TetheringRequest, Executor, StartTetheringCallback) + */ + public static final int TETHERING_ETHERNET = 5; + public static final int TETHER_ERROR_NO_ERROR = 0; public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java index baee99a5a5..30ae884771 100644 --- a/Tethering/src/android/net/ip/IpServer.java +++ b/Tethering/src/android/net/ip/IpServer.java @@ -93,6 +93,8 @@ public class IpServer extends StateMachine { private static final int WIFI_HOST_IFACE_PREFIX_LENGTH = 24; private static final String WIFI_P2P_IFACE_ADDR = "192.168.49.1"; private static final int WIFI_P2P_IFACE_PREFIX_LENGTH = 24; + private static final String ETHERNET_IFACE_ADDR = "192.168.50.1"; + private static final int ETHERNET_IFACE_PREFIX_LENGTH = 24; // TODO: have PanService use some visible version of this constant private static final String BLUETOOTH_IFACE_ADDR = "192.168.44.1"; @@ -426,6 +428,10 @@ public class IpServer extends StateMachine { } else if (mInterfaceType == TetheringManager.TETHERING_WIFI_P2P) { srvAddr = (Inet4Address) parseNumericAddress(WIFI_P2P_IFACE_ADDR); prefixLen = WIFI_P2P_IFACE_PREFIX_LENGTH; + } else if (mInterfaceType == TetheringManager.TETHERING_ETHERNET) { + // TODO: randomize address for tethering too, similarly to wifi + srvAddr = (Inet4Address) parseNumericAddress(ETHERNET_IFACE_ADDR); + prefixLen = ETHERNET_IFACE_PREFIX_LENGTH; } else { // BT configures the interface elsewhere: only start DHCP. // TODO: make all tethering types behave the same way, and delete the bluetooth diff --git a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java index f4d083194d..ddf6d408e9 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java +++ b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java @@ -30,6 +30,7 @@ import static android.net.TetheringManager.EXTRA_ACTIVE_TETHER; import static android.net.TetheringManager.EXTRA_AVAILABLE_TETHER; import static android.net.TetheringManager.EXTRA_ERRORED_TETHER; import static android.net.TetheringManager.TETHERING_BLUETOOTH; +import static android.net.TetheringManager.TETHERING_ETHERNET; import static android.net.TetheringManager.TETHERING_INVALID; import static android.net.TetheringManager.TETHERING_NCM; import static android.net.TetheringManager.TETHERING_USB; @@ -68,6 +69,7 @@ import android.content.IntentFilter; import android.content.res.Resources; import android.hardware.usb.UsbManager; import android.net.ConnectivityManager; +import android.net.EthernetManager; import android.net.IIntResultListener; import android.net.INetd; import android.net.ITetheringEventCallback; @@ -112,6 +114,7 @@ import android.util.SparseArray; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.MessageUtils; @@ -213,6 +216,13 @@ public class Tethering { private TetherStatesParcel mTetherStatesParcel; private boolean mDataSaverEnabled = false; + @GuardedBy("mPublicSync") + private EthernetManager.TetheredInterfaceRequest mEthernetIfaceRequest; + @GuardedBy("mPublicSync") + private String mConfiguredEthernetIface; + @GuardedBy("mPublicSync") + private EthernetCallback mEthernetCallback; + public Tethering(TetheringDependencies deps) { mLog.mark("Tethering.constructed"); mDeps = deps; @@ -463,6 +473,10 @@ public class Tethering { result = setNcmTethering(enable); sendTetherResult(listener, result); break; + case TETHERING_ETHERNET: + result = setEthernetTethering(enable); + sendTetherResult(listener, result); + break; default: Log.w(TAG, "Invalid tether type."); sendTetherResult(listener, TETHER_ERROR_UNKNOWN_IFACE); @@ -539,6 +553,57 @@ public class Tethering { }, BluetoothProfile.PAN); } + private int setEthernetTethering(final boolean enable) { + final EthernetManager em = (EthernetManager) mContext.getSystemService( + Context.ETHERNET_SERVICE); + synchronized (mPublicSync) { + if (enable) { + mEthernetCallback = new EthernetCallback(); + mEthernetIfaceRequest = em.requestTetheredInterface(mEthernetCallback); + } else { + if (mConfiguredEthernetIface != null) { + stopEthernetTetheringLocked(); + mEthernetIfaceRequest.release(); + } + mEthernetCallback = null; + } + } + return TETHER_ERROR_NO_ERROR; + } + + private void stopEthernetTetheringLocked() { + if (mConfiguredEthernetIface == null) return; + changeInterfaceState(mConfiguredEthernetIface, IpServer.STATE_AVAILABLE); + stopTrackingInterfaceLocked(mConfiguredEthernetIface); + mConfiguredEthernetIface = null; + } + + private class EthernetCallback implements EthernetManager.TetheredInterfaceCallback { + @Override + public void onAvailable(String iface) { + synchronized (mPublicSync) { + if (this != mEthernetCallback) { + // Ethernet callback arrived after Ethernet tethering stopped. Ignore. + return; + } + maybeTrackNewInterfaceLocked(iface, TETHERING_ETHERNET); + changeInterfaceState(iface, IpServer.STATE_TETHERED); + mConfiguredEthernetIface = iface; + } + } + + @Override + public void onUnavailable() { + synchronized (mPublicSync) { + if (this != mEthernetCallback) { + // onAvailable called after stopping Ethernet tethering. + return; + } + stopEthernetTetheringLocked(); + } + } + } + int tether(String iface) { return tether(iface, IpServer.STATE_TETHERED); } @@ -589,6 +654,7 @@ public class Tethering { stopTethering(TETHERING_WIFI_P2P); stopTethering(TETHERING_USB); stopTethering(TETHERING_BLUETOOTH); + stopTethering(TETHERING_ETHERNET); } int getLastTetherError(String iface) { diff --git a/Tethering/tests/unit/Android.bp b/Tethering/tests/unit/Android.bp index 53782fed1c..13174c5bb5 100644 --- a/Tethering/tests/unit/Android.bp +++ b/Tethering/tests/unit/Android.bp @@ -19,6 +19,7 @@ android_test { certificate: "platform", srcs: [ "src/**/*.java", + "src/**/*.kt", ], test_suites: [ "device-tests", diff --git a/Tethering/tests/unit/AndroidManifest.xml b/Tethering/tests/unit/AndroidManifest.xml index 0a1cdd35b1..530bc0788a 100644 --- a/Tethering/tests/unit/AndroidManifest.xml +++ b/Tethering/tests/unit/AndroidManifest.xml @@ -16,6 +16,8 @@ + + From 60f090a8ff77ea75445bb7d1fdcd57b8edd737c3 Mon Sep 17 00:00:00 2001 From: Paul Trautrim Date: Thu, 23 Jan 2020 14:55:57 +0900 Subject: [PATCH 028/188] Add implementation of getInterfaceHash() Bug: 136065010 Test: m Change-Id: I314f19aeca82cc8653eab71c9526ea7a208e6b50 --- Tethering/src/android/net/dhcp/DhcpServerCallbacks.java | 5 +++++ Tethering/src/android/net/ip/IpServer.java | 5 +++++ .../android/net/util/BaseNetdUnsolicitedEventListener.java | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/Tethering/src/android/net/dhcp/DhcpServerCallbacks.java b/Tethering/src/android/net/dhcp/DhcpServerCallbacks.java index 7c41377985..9fda1257b4 100644 --- a/Tethering/src/android/net/dhcp/DhcpServerCallbacks.java +++ b/Tethering/src/android/net/dhcp/DhcpServerCallbacks.java @@ -28,4 +28,9 @@ public abstract class DhcpServerCallbacks extends IDhcpServerCallbacks.Stub { public int getInterfaceVersion() { return IDhcpServerCallbacks.VERSION; } + + @Override + public String getInterfaceHash() { + return IDhcpServerCallbacks.HASH; + } } diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java index 190d250986..f39e7af43e 100644 --- a/Tethering/src/android/net/ip/IpServer.java +++ b/Tethering/src/android/net/ip/IpServer.java @@ -301,6 +301,11 @@ public class IpServer extends StateMachine { public int getInterfaceVersion() { return this.VERSION; } + + @Override + public String getInterfaceHash() { + return this.HASH; + } } private class DhcpServerCallbacksImpl extends DhcpServerCallbacks { diff --git a/Tethering/src/android/net/util/BaseNetdUnsolicitedEventListener.java b/Tethering/src/android/net/util/BaseNetdUnsolicitedEventListener.java index 3218c0b387..b1ffdb01f5 100644 --- a/Tethering/src/android/net/util/BaseNetdUnsolicitedEventListener.java +++ b/Tethering/src/android/net/util/BaseNetdUnsolicitedEventListener.java @@ -67,4 +67,9 @@ public class BaseNetdUnsolicitedEventListener extends INetdUnsolicitedEventListe public int getInterfaceVersion() { return INetdUnsolicitedEventListener.VERSION; } + + @Override + public String getInterfaceHash() { + return INetdUnsolicitedEventListener.HASH; + } } From d2ab0eeb134971f07bcbd5b3fd24284230495ffc Mon Sep 17 00:00:00 2001 From: Jimmy Chen Date: Tue, 4 Feb 2020 14:25:42 +0800 Subject: [PATCH 029/188] Tethering: add p2p regex for shared p2p group interface For the device does not support separate group interface, there is only one interface, ex. p2p0, for both device and group interface. Fixes: 141382930 Bug: 141382930 Test: atest TetheringTests launch GO on walleye Change-Id: I064dd8d5dc372411ee771496813ae7c43ed037bc --- Tethering/res/values/config.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/Tethering/res/values/config.xml b/Tethering/res/values/config.xml index c489cbcd5a..4af5c53719 100644 --- a/Tethering/res/values/config.xml +++ b/Tethering/res/values/config.xml @@ -47,6 +47,7 @@ should be empty. An example would be "p2p-p2p\\d-.*" --> "p2p-p2p\\d-.*" + "p2p\\d" com.android.settings/.wifi.tether.TetherService + + + + + + USB;com.android.networkstack.tethering:drawable/stat_sys_tether_usb + BT;com.android.networkstack.tethering:drawable/stat_sys_tether_bluetooth + WIFI|USB,WIFI|BT,USB|BT,WIFI|USB|BT;com.android.networkstack.tethering:drawable/stat_sys_tether_general + + + @string/tethered_notification_title + + @string/tethered_notification_message diff --git a/Tethering/res/values/overlayable.xml b/Tethering/res/values/overlayable.xml index fe025c7ac9..bbba3f30a2 100644 --- a/Tethering/res/values/overlayable.xml +++ b/Tethering/res/values/overlayable.xml @@ -16,6 +16,7 @@ + @@ -31,6 +32,45 @@ + + + + + + + + diff --git a/Tethering/res/values/strings.xml b/Tethering/res/values/strings.xml index 792bce9fc3..ba98a66ff7 100644 --- a/Tethering/res/values/strings.xml +++ b/Tethering/res/values/strings.xml @@ -15,19 +15,21 @@ --> - + Tethering or hotspot active - + Tap to set up. - + Tethering is disabled - + Contact your admin for details - + + Hotspot & tethering status \ No newline at end of file diff --git a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java index f89da849ea..3d8dbab7d4 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java +++ b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java @@ -59,10 +59,8 @@ import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED; import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; -import android.app.Notification; -import android.app.NotificationChannel; -import android.app.NotificationManager; -import android.app.PendingIntent; +import static com.android.server.connectivity.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE; + import android.app.usage.NetworkStatsManager; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothPan; @@ -72,7 +70,6 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.res.Resources; import android.hardware.usb.UsbManager; import android.net.ConnectivityManager; import android.net.EthernetManager; @@ -128,7 +125,6 @@ import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.MessageUtils; import com.android.internal.util.State; import com.android.internal.util.StateMachine; -import com.android.networkstack.tethering.R; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -224,14 +220,13 @@ public class Tethering { private final ActiveDataSubIdListener mActiveDataSubIdListener; private final ConnectedClientsTracker mConnectedClientsTracker; private final TetheringThreadExecutor mExecutor; + private final TetheringNotificationUpdater mNotificationUpdater; private int mActiveDataSubId = INVALID_SUBSCRIPTION_ID; // All the usage of mTetheringEventCallback should run in the same thread. private ITetheringEventCallback mTetheringEventCallback = null; private volatile TetheringConfiguration mConfig; private InterfaceSet mCurrentUpstreamIfaceSet; - private Notification.Builder mTetheredNotificationBuilder; - private int mLastNotificationId; private boolean mRndisEnabled; // track the RNDIS function enabled state // True iff. WiFi tethering should be started when soft AP is ready. @@ -255,6 +250,7 @@ public class Tethering { mContext = mDeps.getContext(); mNetd = mDeps.getINetd(mContext); mLooper = mDeps.getTetheringLooper(); + mNotificationUpdater = mDeps.getNotificationUpdater(mContext); mPublicSync = new Object(); @@ -738,13 +734,10 @@ public class Tethering { final ArrayList erroredList = new ArrayList<>(); final ArrayList lastErrorList = new ArrayList<>(); - boolean wifiTethered = false; - boolean usbTethered = false; - boolean bluetoothTethered = false; - final TetheringConfiguration cfg = mConfig; mTetherStatesParcel = new TetherStatesParcel(); + int downstreamTypesMask = DOWNSTREAM_NONE; synchronized (mPublicSync) { for (int i = 0; i < mTetherStates.size(); i++) { TetherState tetherState = mTetherStates.valueAt(i); @@ -758,11 +751,11 @@ public class Tethering { localOnlyList.add(iface); } else if (tetherState.lastState == IpServer.STATE_TETHERED) { if (cfg.isUsb(iface)) { - usbTethered = true; + downstreamTypesMask |= (1 << TETHERING_USB); } else if (cfg.isWifi(iface)) { - wifiTethered = true; + downstreamTypesMask |= (1 << TETHERING_WIFI); } else if (cfg.isBluetooth(iface)) { - bluetoothTethered = true; + downstreamTypesMask |= (1 << TETHERING_BLUETOOTH); } tetherList.add(iface); } @@ -796,98 +789,7 @@ public class Tethering { "error", TextUtils.join(",", erroredList))); } - if (usbTethered) { - if (wifiTethered || bluetoothTethered) { - showTetheredNotification(R.drawable.stat_sys_tether_general); - } else { - showTetheredNotification(R.drawable.stat_sys_tether_usb); - } - } else if (wifiTethered) { - if (bluetoothTethered) { - showTetheredNotification(R.drawable.stat_sys_tether_general); - } else { - /* We now have a status bar icon for WifiTethering, so drop the notification */ - clearTetheredNotification(); - } - } else if (bluetoothTethered) { - showTetheredNotification(R.drawable.stat_sys_tether_bluetooth); - } else { - clearTetheredNotification(); - } - } - - private void showTetheredNotification(int id) { - showTetheredNotification(id, true); - } - - @VisibleForTesting - protected void showTetheredNotification(int id, boolean tetheringOn) { - NotificationManager notificationManager = - (NotificationManager) mContext.createContextAsUser(UserHandle.ALL, 0) - .getSystemService(Context.NOTIFICATION_SERVICE); - if (notificationManager == null) { - return; - } - final NotificationChannel channel = new NotificationChannel( - "TETHERING_STATUS", - mContext.getResources().getString(R.string.notification_channel_tethering_status), - NotificationManager.IMPORTANCE_LOW); - notificationManager.createNotificationChannel(channel); - - if (mLastNotificationId != 0) { - if (mLastNotificationId == id) { - return; - } - notificationManager.cancel(null, mLastNotificationId); - mLastNotificationId = 0; - } - - Intent intent = new Intent(); - intent.setClassName("com.android.settings", "com.android.settings.TetherSettings"); - intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); - - PendingIntent pi = PendingIntent.getActivity( - mContext.createContextAsUser(UserHandle.CURRENT, 0), 0, intent, 0, null); - - Resources r = mContext.getResources(); - final CharSequence title; - final CharSequence message; - - if (tetheringOn) { - title = r.getText(R.string.tethered_notification_title); - message = r.getText(R.string.tethered_notification_message); - } else { - title = r.getText(R.string.disable_tether_notification_title); - message = r.getText(R.string.disable_tether_notification_message); - } - - if (mTetheredNotificationBuilder == null) { - mTetheredNotificationBuilder = new Notification.Builder(mContext, channel.getId()); - mTetheredNotificationBuilder.setWhen(0) - .setOngoing(true) - .setColor(mContext.getColor( - android.R.color.system_notification_accent_color)) - .setVisibility(Notification.VISIBILITY_PUBLIC) - .setCategory(Notification.CATEGORY_STATUS); - } - mTetheredNotificationBuilder.setSmallIcon(id) - .setContentTitle(title) - .setContentText(message) - .setContentIntent(pi); - mLastNotificationId = id; - - notificationManager.notify(null, mLastNotificationId, mTetheredNotificationBuilder.build()); - } - - @VisibleForTesting - protected void clearTetheredNotification() { - NotificationManager notificationManager = - (NotificationManager) mContext.createContextAsUser(UserHandle.ALL, 0) - .getSystemService(Context.NOTIFICATION_SERVICE); - if (notificationManager != null && mLastNotificationId != 0) { - notificationManager.cancel(null, mLastNotificationId); - mLastNotificationId = 0; - } + mNotificationUpdater.onDownstreamChanged(downstreamTypesMask); } private class StateReceiver extends BroadcastReceiver { @@ -1081,12 +983,10 @@ public class Tethering { return; } - mWrapper.clearTetheredNotification(); + // TODO: Add user restrictions notification. final boolean isTetheringActiveOnDevice = (mWrapper.getTetheredIfaces().length != 0); if (newlyDisallowed && isTetheringActiveOnDevice) { - mWrapper.showTetheredNotification( - R.drawable.stat_sys_tether_general, false); mWrapper.untetherAll(); // TODO(b/148139325): send tetheringSupported on restriction change } diff --git a/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java b/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java index e019c3aca2..0330dad6a1 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java +++ b/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java @@ -26,6 +26,8 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; +import androidx.annotation.NonNull; + import com.android.internal.util.StateMachine; import java.util.ArrayList; @@ -101,6 +103,13 @@ public abstract class TetheringDependencies { (IBinder) context.getSystemService(Context.NETD_SERVICE)); } + /** + * Get a reference to the TetheringNotificationUpdater to be used by tethering. + */ + public TetheringNotificationUpdater getNotificationUpdater(@NonNull final Context ctx) { + return new TetheringNotificationUpdater(ctx); + } + /** * Get tethering thread looper. */ diff --git a/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java b/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java new file mode 100644 index 0000000000..b97f75268a --- /dev/null +++ b/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.connectivity.tethering; + +import static android.net.TetheringManager.TETHERING_BLUETOOTH; +import static android.net.TetheringManager.TETHERING_USB; +import static android.net.TetheringManager.TETHERING_WIFI; + +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.os.UserHandle; +import android.provider.Settings; +import android.text.TextUtils; +import android.util.Log; +import android.util.SparseArray; + +import androidx.annotation.ArrayRes; +import androidx.annotation.DrawableRes; +import androidx.annotation.IntRange; +import androidx.annotation.NonNull; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.networkstack.tethering.R; + +/** + * A class to display tethering-related notifications. + * + *

This class is not thread safe, it is intended to be used only from the tethering handler + * thread. However the constructor is an exception, as it is called on another thread ; + * therefore for thread safety all members of this class MUST either be final or initialized + * to their default value (0, false or null). + * + * @hide + */ +public class TetheringNotificationUpdater { + private static final String TAG = TetheringNotificationUpdater.class.getSimpleName(); + private static final String CHANNEL_ID = "TETHERING_STATUS"; + private static final boolean NOTIFY_DONE = true; + private static final boolean NO_NOTIFY = false; + // Id to update and cancel tethering notification. Must be unique within the tethering app. + private static final int NOTIFY_ID = 20191115; + @VisibleForTesting + static final int NO_ICON_ID = 0; + @VisibleForTesting + static final int DOWNSTREAM_NONE = 0; + private final Context mContext; + private final NotificationManager mNotificationManager; + private final NotificationChannel mChannel; + // Downstream type is one of ConnectivityManager.TETHERING_* constants, 0 1 or 2. + // This value has to be made 1 2 and 4, and OR'd with the others. + // WARNING : the constructor is called on a different thread. Thread safety therefore + // relies on this value being initialized to 0, and not any other value. If you need + // to change this, you will need to change the thread where the constructor is invoked, + // or to introduce synchronization. + private int mDownstreamTypesMask = DOWNSTREAM_NONE; + + public TetheringNotificationUpdater(@NonNull final Context context) { + mContext = context; + mNotificationManager = (NotificationManager) context.createContextAsUser(UserHandle.ALL, 0) + .getSystemService(Context.NOTIFICATION_SERVICE); + mChannel = new NotificationChannel( + CHANNEL_ID, + context.getResources().getString(R.string.notification_channel_tethering_status), + NotificationManager.IMPORTANCE_LOW); + mNotificationManager.createNotificationChannel(mChannel); + } + + /** Called when downstream has changed */ + public void onDownstreamChanged(@IntRange(from = 0, to = 7) final int downstreamTypesMask) { + if (mDownstreamTypesMask == downstreamTypesMask) return; + mDownstreamTypesMask = downstreamTypesMask; + updateNotification(); + } + + private void updateNotification() { + final boolean tetheringInactive = mDownstreamTypesMask <= DOWNSTREAM_NONE; + + if (tetheringInactive || setupNotification() == NO_NOTIFY) { + clearNotification(); + } + } + + private void clearNotification() { + mNotificationManager.cancel(null /* tag */, NOTIFY_ID); + } + + /** + * Returns the downstream types mask which convert from given string. + * + * @param types This string has to be made by "WIFI", "USB", "BT", and OR'd with the others. + * + * @return downstream types mask value. + */ + @IntRange(from = 0, to = 7) + private int getDownstreamTypesMask(@NonNull final String types) { + int downstreamTypesMask = DOWNSTREAM_NONE; + final String[] downstreams = types.split("\\|"); + for (String downstream : downstreams) { + if ("USB".equals(downstream.trim())) { + downstreamTypesMask |= (1 << TETHERING_USB); + } else if ("WIFI".equals(downstream.trim())) { + downstreamTypesMask |= (1 << TETHERING_WIFI); + } else if ("BT".equals(downstream.trim())) { + downstreamTypesMask |= (1 << TETHERING_BLUETOOTH); + } + } + return downstreamTypesMask; + } + + /** + * Returns the icons {@link android.util.SparseArray} which get from given string-array resource + * id. + * + * @param id String-array resource id + * + * @return {@link android.util.SparseArray} with downstream types and icon id info. + */ + @NonNull + private SparseArray getIcons(@ArrayRes int id) { + final Resources res = mContext.getResources(); + final String[] array = res.getStringArray(id); + final SparseArray icons = new SparseArray<>(); + for (String config : array) { + if (TextUtils.isEmpty(config)) continue; + + final String[] elements = config.split(";"); + if (elements.length != 2) { + Log.wtf(TAG, + "Unexpected format in Tethering notification configuration : " + config); + continue; + } + + final String[] types = elements[0].split(","); + for (String type : types) { + int mask = getDownstreamTypesMask(type); + if (mask == DOWNSTREAM_NONE) continue; + icons.put(mask, res.getIdentifier( + elements[1].trim(), null /* defType */, null /* defPackage */)); + } + } + return icons; + } + + private boolean setupNotification() { + final Resources res = mContext.getResources(); + final SparseArray downstreamIcons = getIcons(R.array.tethering_notification_icons); + + final int iconId = downstreamIcons.get(mDownstreamTypesMask, NO_ICON_ID); + if (iconId == NO_ICON_ID) return NO_NOTIFY; + + final String title = res.getString(R.string.tethering_notification_title); + final String message = res.getString(R.string.tethering_notification_message); + + showNotification(iconId, title, message); + return NOTIFY_DONE; + } + + private void showNotification(@DrawableRes final int iconId, @NonNull final String title, + @NonNull final String message) { + final Intent intent = new Intent(Settings.ACTION_TETHER_SETTINGS); + final PendingIntent pi = PendingIntent.getActivity( + mContext.createContextAsUser(UserHandle.CURRENT, 0), + 0 /* requestCode */, intent, 0 /* flags */, null /* options */); + final Notification notification = + new Notification.Builder(mContext, mChannel.getId()) + .setSmallIcon(iconId) + .setContentTitle(title) + .setContentText(message) + .setOngoing(true) + .setColor(mContext.getColor( + android.R.color.system_notification_accent_color)) + .setVisibility(Notification.VISIBILITY_PUBLIC) + .setCategory(Notification.CATEGORY_STATUS) + .setContentIntent(pi) + .build(); + + mNotificationManager.notify(null /* tag */, NOTIFY_ID, notification); + } +} diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java index af7ad662b8..820c852a27 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java @@ -46,6 +46,8 @@ import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED; import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; +import static com.android.server.connectivity.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE; + import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -53,7 +55,6 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.notNull; -import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.any; @@ -188,6 +189,7 @@ public class TetheringTest { @Mock private NetworkRequest mNetworkRequest; @Mock private ConnectivityManager mCm; @Mock private EthernetManager mEm; + @Mock private TetheringNotificationUpdater mNotificationUpdater; private final MockIpServerDependencies mIpServerDependencies = spy(new MockIpServerDependencies()); @@ -207,6 +209,7 @@ public class TetheringTest { private PhoneStateListener mPhoneStateListener; private InterfaceConfigurationParcel mInterfaceConfiguration; + private class TestContext extends BroadcastInterceptingContext { TestContext(Context base) { super(base); @@ -249,11 +252,6 @@ public class TetheringTest { if (TelephonyManager.class.equals(serviceClass)) return Context.TELEPHONY_SERVICE; return super.getSystemServiceName(serviceClass); } - - @Override - public Context createContextAsUser(UserHandle user, int flags) { - return mContext; - } } public class MockIpServerDependencies extends IpServer.Dependencies { @@ -315,12 +313,10 @@ public class TetheringTest { public class MockTetheringDependencies extends TetheringDependencies { StateMachine mUpstreamNetworkMonitorMasterSM; ArrayList mIpv6CoordinatorNotifyList; - int mIsTetheringSupportedCalls; public void reset() { mUpstreamNetworkMonitorMasterSM = null; mIpv6CoordinatorNotifyList = null; - mIsTetheringSupportedCalls = 0; } @Override @@ -354,7 +350,6 @@ public class TetheringTest { @Override public boolean isTetheringSupported() { - mIsTetheringSupportedCalls++; return true; } @@ -384,6 +379,11 @@ public class TetheringTest { // TODO: add test for bluetooth tethering. return null; } + + @Override + public TetheringNotificationUpdater getNotificationUpdater(Context ctx) { + return mNotificationUpdater; + } } private static UpstreamNetworkState buildMobileUpstreamState(boolean withIPv4, @@ -472,7 +472,6 @@ public class TetheringTest { when(mOffloadHardwareInterface.getForwardedStats(any())).thenReturn(mForwardedStats); mServiceContext = new TestContext(mContext); - when(mContext.getSystemService(Context.NOTIFICATION_SERVICE)).thenReturn(null); mContentResolver = new MockContentResolver(mServiceContext); mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); mIntents = new Vector<>(); @@ -605,7 +604,8 @@ public class TetheringTest { // it creates a IpServer and sends out a broadcast indicating that the // interface is "available". if (emulateInterfaceStatusChanged) { - assertEquals(1, mTetheringDependencies.mIsTetheringSupportedCalls); + // There is 1 IpServer state change event: STATE_AVAILABLE + verify(mNotificationUpdater, times(1)).onDownstreamChanged(DOWNSTREAM_NONE); verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER); verify(mWifiManager).updateInterfaceIpState( TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED); @@ -689,9 +689,8 @@ public class TetheringTest { verifyNoMoreInteractions(mWifiManager); verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_LOCAL_ONLY); verify(mUpstreamNetworkMonitor, times(1)).startObserveAllNetworks(); - // This will be called twice, one is on entering IpServer.STATE_AVAILABLE, - // and another one is on IpServer.STATE_TETHERED/IpServer.STATE_LOCAL_ONLY. - assertEquals(2, mTetheringDependencies.mIsTetheringSupportedCalls); + // There are 2 IpServer state change events: STATE_AVAILABLE -> STATE_LOCAL_ONLY + verify(mNotificationUpdater, times(2)).onDownstreamChanged(DOWNSTREAM_NONE); // Emulate externally-visible WifiManager effects, when hotspot mode // is being torn down. @@ -917,7 +916,8 @@ public class TetheringTest { sendWifiApStateChanged(WIFI_AP_STATE_ENABLED); mLooper.dispatchAll(); - assertEquals(1, mTetheringDependencies.mIsTetheringSupportedCalls); + // There is 1 IpServer state change event: STATE_AVAILABLE + verify(mNotificationUpdater, times(1)).onDownstreamChanged(DOWNSTREAM_NONE); verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER); verify(mWifiManager).updateInterfaceIpState( TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED); @@ -961,9 +961,9 @@ public class TetheringTest { // In tethering mode, in the default configuration, an explicit request // for a mobile network is also made. verify(mUpstreamNetworkMonitor, times(1)).registerMobileNetworkRequest(); - // This will be called twice, one is on entering IpServer.STATE_AVAILABLE, - // and another one is on IpServer.STATE_TETHERED/IpServer.STATE_LOCAL_ONLY. - assertEquals(2, mTetheringDependencies.mIsTetheringSupportedCalls); + // There are 2 IpServer state change events: STATE_AVAILABLE -> STATE_TETHERED + verify(mNotificationUpdater, times(1)).onDownstreamChanged(DOWNSTREAM_NONE); + verify(mNotificationUpdater, times(1)).onDownstreamChanged(eq(1 << TETHERING_WIFI)); ///// // We do not currently emulate any upstream being found. @@ -1034,9 +1034,10 @@ public class TetheringTest { TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED); verify(mWifiManager).updateInterfaceIpState( TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED); - // There are 3 state change event: - // AVAILABLE -> STATE_TETHERED -> STATE_AVAILABLE. - assertEquals(3, mTetheringDependencies.mIsTetheringSupportedCalls); + // There are 3 IpServer state change event: + // STATE_AVAILABLE -> STATE_TETHERED -> STATE_AVAILABLE. + verify(mNotificationUpdater, times(2)).onDownstreamChanged(DOWNSTREAM_NONE); + verify(mNotificationUpdater, times(1)).onDownstreamChanged(eq(1 << TETHERING_WIFI)); verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER); // This is called, but will throw. verify(mNetd, times(1)).ipfwdEnableForwarding(TETHERING_NAME); @@ -1070,9 +1071,6 @@ public class TetheringTest { ural.onUserRestrictionsChanged(); - verify(mockTethering, times(expectedInteractionsWithShowNotification)) - .showTetheredNotification(anyInt(), eq(false)); - verify(mockTethering, times(expectedInteractionsWithShowNotification)) .untetherAll(); } @@ -1429,9 +1427,8 @@ public class TetheringTest { verifyNoMoreInteractions(mNetd); verifyTetheringBroadcast(TEST_P2P_IFNAME, EXTRA_ACTIVE_LOCAL_ONLY); verify(mUpstreamNetworkMonitor, times(1)).startObserveAllNetworks(); - // This will be called twice, one is on entering IpServer.STATE_AVAILABLE, - // and another one is on IpServer.STATE_TETHERED/IpServer.STATE_LOCAL_ONLY. - assertEquals(2, mTetheringDependencies.mIsTetheringSupportedCalls); + // There are 2 IpServer state change events: STATE_AVAILABLE -> STATE_LOCAL_ONLY + verify(mNotificationUpdater, times(2)).onDownstreamChanged(DOWNSTREAM_NONE); assertEquals(TETHER_ERROR_NO_ERROR, mTethering.getLastTetherError(TEST_P2P_IFNAME)); From 943fb5b68630c43223769f37d65e05cca93fb9ba Mon Sep 17 00:00:00 2001 From: Automerger Merge Worker Date: Mon, 16 Mar 2020 06:21:59 +0000 Subject: [PATCH 047/188] Change TetheringConstants class to final Bug: 151322331 Test: m doc-comment-check-docs Change-Id: Ieca36d81b7799988b8dbb9c0d22de690136303ab Merged-In: Ia02be3d1d91a08ae4a56b25560ed448c96a693db (cherry picked from commit 5e2740b24658ba340a6b47a46c89cfd642afe394) --- .../common/TetheringLib/src/android/net/TetheringConstants.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java b/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java index a18f5da60c..fd6f171487 100644 --- a/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java +++ b/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java @@ -32,7 +32,7 @@ import android.os.ResultReceiver; * @hide */ @SystemApi(client = MODULE_LIBRARIES) -public class TetheringConstants { +public final class TetheringConstants { /** An explicit private class to avoid exposing constructor.*/ private TetheringConstants() { } From 0010ca0c701ff40ea45ca7751e47a29d58e6b050 Mon Sep 17 00:00:00 2001 From: Automerger Merge Worker Date: Mon, 16 Mar 2020 04:06:46 +0000 Subject: [PATCH 048/188] Cleanup the TetheredClients API Add comments to getters as requested in API review, and remove the expirationTime private field that was planned to be replaced with LinkAddress expiration. Test: atest TetheringTests Fixes: 150878126 Change-Id: Iecf65859cdeeaac2fa7b817b4f505c510424ac89 Merged-In: Iecf65859cdeeaac2fa7b817b4f505c510424ac89 (cherry picked from commit 594d0eae38c13e2bb03de0b3ae1f8781991c321e) --- .../src/android/net/TetheredClient.java | 41 ++++++++++++------- Tethering/src/android/net/ip/IpServer.java | 7 +++- .../src/android/net/TetheredClientTest.kt | 13 +++++- .../tethering/ConnectedClientsTrackerTest.kt | 17 +++++--- 4 files changed, 53 insertions(+), 25 deletions(-) diff --git a/Tethering/common/TetheringLib/src/android/net/TetheredClient.java b/Tethering/common/TetheringLib/src/android/net/TetheredClient.java index 8b8b9e57a5..48be0d9642 100644 --- a/Tethering/common/TetheringLib/src/android/net/TetheredClient.java +++ b/Tethering/common/TetheringLib/src/android/net/TetheredClient.java @@ -64,16 +64,26 @@ public final class TetheredClient implements Parcelable { dest.writeInt(mTetheringType); } + /** + * Get the MAC address used to identify the client. + */ @NonNull public MacAddress getMacAddress() { return mMacAddress; } + /** + * Get information on the list of addresses that are associated with the client. + */ @NonNull public List getAddresses() { return new ArrayList<>(mAddresses); } + /** + * Get the type of tethering used by the client. + * @return one of the {@code TetheringManager#TETHERING_*} constants. + */ public int getTetheringType() { return mTetheringType; } @@ -115,45 +125,47 @@ public final class TetheredClient implements Parcelable { private final LinkAddress mAddress; @Nullable private final String mHostname; - // TODO: use LinkAddress expiration time once it is supported - private final long mExpirationTime; /** @hide */ public AddressInfo(@NonNull LinkAddress address, @Nullable String hostname) { - this(address, hostname, 0); - } - - /** @hide */ - public AddressInfo(@NonNull LinkAddress address, String hostname, long expirationTime) { this.mAddress = address; this.mHostname = hostname; - this.mExpirationTime = expirationTime; } private AddressInfo(Parcel in) { - this(in.readParcelable(null), in.readString(), in.readLong()); + this(in.readParcelable(null), in.readString()); } @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeParcelable(mAddress, flags); dest.writeString(mHostname); - dest.writeLong(mExpirationTime); } + /** + * Get the link address (including prefix length and lifetime) used by the client. + * + * This may be an IPv4 or IPv6 address. + */ @NonNull public LinkAddress getAddress() { return mAddress; } + /** + * Get the hostname that was advertised by the client when obtaining its address, if any. + */ @Nullable public String getHostname() { return mHostname; } - /** @hide TODO: use expiration time in LinkAddress */ + /** + * Get the expiration time of the address assigned to the client. + * @hide + */ public long getExpirationTime() { - return mExpirationTime; + return mAddress.getExpirationTime(); } @Override @@ -163,7 +175,7 @@ public final class TetheredClient implements Parcelable { @Override public int hashCode() { - return Objects.hash(mAddress, mHostname, mExpirationTime); + return Objects.hash(mAddress, mHostname); } @Override @@ -173,8 +185,7 @@ public final class TetheredClient implements Parcelable { // Use .equals() for addresses as all changes, including address expiry changes, // should be included. return other.mAddress.equals(mAddress) - && Objects.equals(mHostname, other.mHostname) - && mExpirationTime == other.mExpirationTime; + && Objects.equals(mHostname, other.mHostname); } @NonNull diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java index 38f8609e21..6c0c432d46 100644 --- a/Tethering/src/android/net/ip/IpServer.java +++ b/Tethering/src/android/net/ip/IpServer.java @@ -24,6 +24,7 @@ import static android.net.util.NetworkConstants.FF; import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH; import static android.net.util.NetworkConstants.asByte; import static android.net.util.TetheringMessageBase.BASE_IPSERVER; +import static android.system.OsConstants.RT_SCOPE_UNIVERSE; import android.net.INetd; import android.net.INetworkStackStatusCallback; @@ -448,7 +449,9 @@ public class IpServer extends StateMachine { final ArrayList leases = new ArrayList<>(); for (DhcpLeaseParcelable lease : leaseParcelables) { final LinkAddress address = new LinkAddress( - intToInet4AddressHTH(lease.netAddr), lease.prefixLength); + intToInet4AddressHTH(lease.netAddr), lease.prefixLength, + 0 /* flags */, RT_SCOPE_UNIVERSE /* as per RFC6724#3.2 */, + lease.expTime /* deprecationTime */, lease.expTime /* expirationTime */); final MacAddress macAddress; try { @@ -460,7 +463,7 @@ public class IpServer extends StateMachine { } final TetheredClient.AddressInfo addressInfo = new TetheredClient.AddressInfo( - address, lease.hostname, lease.expTime); + address, lease.hostname); leases.add(new TetheredClient( macAddress, Collections.singletonList(addressInfo), diff --git a/Tethering/tests/unit/src/android/net/TetheredClientTest.kt b/Tethering/tests/unit/src/android/net/TetheredClientTest.kt index d85389aeb6..a20a0dfd9c 100644 --- a/Tethering/tests/unit/src/android/net/TetheredClientTest.kt +++ b/Tethering/tests/unit/src/android/net/TetheredClientTest.kt @@ -20,6 +20,7 @@ import android.net.InetAddresses.parseNumericAddress import android.net.TetheredClient.AddressInfo import android.net.TetheringManager.TETHERING_BLUETOOTH import android.net.TetheringManager.TETHERING_USB +import android.system.OsConstants.RT_SCOPE_UNIVERSE import androidx.test.filters.SmallTest import androidx.test.runner.AndroidJUnit4 import com.android.testutils.assertParcelSane @@ -30,11 +31,19 @@ import kotlin.test.assertNotEquals private val TEST_MACADDR = MacAddress.fromBytes(byteArrayOf(12, 23, 34, 45, 56, 67)) private val TEST_OTHER_MACADDR = MacAddress.fromBytes(byteArrayOf(23, 34, 45, 56, 67, 78)) -private val TEST_ADDR1 = LinkAddress(parseNumericAddress("192.168.113.3"), 24) -private val TEST_ADDR2 = LinkAddress(parseNumericAddress("fe80::1:2:3"), 64) +private val TEST_ADDR1 = makeLinkAddress("192.168.113.3", prefixLength = 24, expTime = 123L) +private val TEST_ADDR2 = makeLinkAddress("fe80::1:2:3", prefixLength = 64, expTime = 456L) private val TEST_ADDRINFO1 = AddressInfo(TEST_ADDR1, "test_hostname") private val TEST_ADDRINFO2 = AddressInfo(TEST_ADDR2, null) +private fun makeLinkAddress(addr: String, prefixLength: Int, expTime: Long) = LinkAddress( + parseNumericAddress(addr), + prefixLength, + 0 /* flags */, + RT_SCOPE_UNIVERSE, + expTime /* deprecationTime */, + expTime /* expirationTime */) + @RunWith(AndroidJUnit4::class) @SmallTest class TetheredClientTest { diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/ConnectedClientsTrackerTest.kt b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/ConnectedClientsTrackerTest.kt index 56f3e21cbf..1cdc3bbb99 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/ConnectedClientsTrackerTest.kt +++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/ConnectedClientsTrackerTest.kt @@ -46,23 +46,28 @@ class ConnectedClientsTrackerTest { private val client1Addr = MacAddress.fromString("01:23:45:67:89:0A") private val client1 = TetheredClient(client1Addr, listOf( - AddressInfo(LinkAddress("192.168.43.44/32"), null /* hostname */, clock.time + 20)), + makeAddrInfo("192.168.43.44/32", null /* hostname */, clock.time + 20)), TETHERING_WIFI) private val wifiClient1 = makeWifiClient(client1Addr) private val client2Addr = MacAddress.fromString("02:34:56:78:90:AB") - private val client2Exp30AddrInfo = AddressInfo( - LinkAddress("192.168.43.45/32"), "my_hostname", clock.time + 30) + private val client2Exp30AddrInfo = makeAddrInfo( + "192.168.43.45/32", "my_hostname", clock.time + 30) private val client2 = TetheredClient(client2Addr, listOf( client2Exp30AddrInfo, - AddressInfo(LinkAddress("2001:db8:12::34/72"), "other_hostname", clock.time + 10)), + makeAddrInfo("2001:db8:12::34/72", "other_hostname", clock.time + 10)), TETHERING_WIFI) private val wifiClient2 = makeWifiClient(client2Addr) private val client3Addr = MacAddress.fromString("03:45:67:89:0A:BC") private val client3 = TetheredClient(client3Addr, - listOf(AddressInfo(LinkAddress("2001:db8:34::34/72"), "other_other_hostname", - clock.time + 10)), + listOf(makeAddrInfo("2001:db8:34::34/72", "other_other_hostname", clock.time + 10)), TETHERING_USB) + private fun makeAddrInfo(addr: String, hostname: String?, expTime: Long) = + LinkAddress(addr).let { + AddressInfo(LinkAddress(it.address, it.prefixLength, it.flags, it.scope, + expTime /* deprecationTime */, expTime /* expirationTime */), hostname) + } + @Test fun testUpdateConnectedClients() { doReturn(emptyList()).`when`(server1).allLeases From 4aa86b782b27216d333092ca7ccd8cd983045322 Mon Sep 17 00:00:00 2001 From: junyulai Date: Fri, 6 Mar 2020 14:50:48 +0800 Subject: [PATCH 049/188] [SP21] Address comments for API council review about aosp/1172143 Test: atest FrameworksNetTests ImsPhoneCallTrackerTest Test: atest TetheringTests NetworkStackTests Test: m doc-comment-check-docs Fix: 148552904 Change-Id: I141393f229e772d2eb9f7c156849e379bd71b845 Merged-In: I141393f229e772d2eb9f7c156849e379bd71b845 (cherry picked from aosp/1253717) --- .../tethering/OffloadController.java | 45 +++++++------- .../tethering/OffloadControllerTest.java | 62 +++++++++---------- 2 files changed, 54 insertions(+), 53 deletions(-) diff --git a/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java b/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java index a402ffa473..d43c5c682f 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java +++ b/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java @@ -39,8 +39,7 @@ import android.net.RouteInfo; import android.net.netlink.ConntrackMessage; import android.net.netlink.NetlinkConstants; import android.net.netlink.NetlinkSocket; -import android.net.netstats.provider.AbstractNetworkStatsProvider; -import android.net.netstats.provider.NetworkStatsProviderCallback; +import android.net.netstats.provider.NetworkStatsProvider; import android.net.util.SharedLog; import android.os.Handler; import android.provider.Settings; @@ -89,8 +88,8 @@ public class OffloadController { private final Handler mHandler; private final OffloadHardwareInterface mHwInterface; private final ContentResolver mContentResolver; - private final @NonNull OffloadTetheringStatsProvider mStatsProvider; - private final @Nullable NetworkStatsProviderCallback mStatsProviderCb; + @Nullable + private final OffloadTetheringStatsProvider mStatsProvider; private final SharedLog mLog; private final HashMap mDownstreams; private boolean mConfigInitialized; @@ -124,19 +123,18 @@ public class OffloadController { mHandler = h; mHwInterface = hwi; mContentResolver = contentResolver; - mStatsProvider = new OffloadTetheringStatsProvider(); mLog = log.forSubComponent(TAG); mDownstreams = new HashMap<>(); mExemptPrefixes = new HashSet<>(); mLastLocalPrefixStrs = new HashSet<>(); - NetworkStatsProviderCallback providerCallback = null; + OffloadTetheringStatsProvider provider = new OffloadTetheringStatsProvider(); try { - providerCallback = nsm.registerNetworkStatsProvider( - getClass().getSimpleName(), mStatsProvider); + nsm.registerNetworkStatsProvider(getClass().getSimpleName(), provider); } catch (RuntimeException e) { Log.wtf(TAG, "Cannot register offload stats provider: " + e); + provider = null; } - mStatsProviderCb = providerCallback; + mStatsProvider = provider; } /** Start hardware offload. */ @@ -185,7 +183,7 @@ public class OffloadController { // and we need to synchronize stats and limits between // software and hardware forwarding. updateStatsForAllUpstreams(); - mStatsProvider.pushTetherStats(); + if (mStatsProvider != null) mStatsProvider.pushTetherStats(); } @Override @@ -198,7 +196,7 @@ public class OffloadController { // limits set take into account any software tethering // traffic that has been happening in the meantime. updateStatsForAllUpstreams(); - mStatsProvider.pushTetherStats(); + if (mStatsProvider != null) mStatsProvider.pushTetherStats(); // [2] (Re)Push all state. computeAndPushLocalPrefixes(UpdateType.FORCE); pushAllDownstreamState(); @@ -217,10 +215,12 @@ public class OffloadController { // TODO: rev the HAL so that it provides an interface name. updateStatsForCurrentUpstream(); - mStatsProvider.pushTetherStats(); - // Push stats to service does not cause the service react to it immediately. - // Inform the service about limit reached. - if (mStatsProviderCb != null) mStatsProviderCb.onLimitReached(); + if (mStatsProvider != null) { + mStatsProvider.pushTetherStats(); + // Push stats to service does not cause the service react to it + // immediately. Inform the service about limit reached. + mStatsProvider.notifyLimitReached(); + } } @Override @@ -263,13 +263,17 @@ public class OffloadController { } @VisibleForTesting - class OffloadTetheringStatsProvider extends AbstractNetworkStatsProvider { + class OffloadTetheringStatsProvider extends NetworkStatsProvider { // These stats must only ever be touched on the handler thread. @NonNull private NetworkStats mIfaceStats = new NetworkStats(0L, 0); @NonNull private NetworkStats mUidStats = new NetworkStats(0L, 0); + /** + * A helper function that collect tether stats from local hashmap. Note that this does not + * invoke binder call. + */ @VisibleForTesting @NonNull NetworkStats getTetherStats(@NonNull StatsType how) { @@ -287,7 +291,7 @@ public class OffloadController { } @Override - public void setLimit(String iface, long quotaBytes) { + public void onSetLimit(String iface, long quotaBytes) { // Listen for all iface is necessary since upstream might be changed after limit // is set. mHandler.post(() -> { @@ -315,13 +319,12 @@ public class OffloadController { */ public void pushTetherStats() { // TODO: remove the accumulated stats and report the diff from HAL directly. - if (null == mStatsProviderCb) return; final NetworkStats ifaceDiff = getTetherStats(StatsType.STATS_PER_IFACE).subtract(mIfaceStats); final NetworkStats uidDiff = getTetherStats(StatsType.STATS_PER_UID).subtract(mUidStats); try { - mStatsProviderCb.onStatsUpdated(0 /* token */, ifaceDiff, uidDiff); + notifyStatsUpdated(0 /* token */, ifaceDiff, uidDiff); mIfaceStats = mIfaceStats.add(ifaceDiff); mUidStats = mUidStats.add(uidDiff); } catch (RuntimeException e) { @@ -330,7 +333,7 @@ public class OffloadController { } @Override - public void requestStatsUpdate(int token) { + public void onRequestStatsUpdate(int token) { // Do not attempt to update stats by querying the offload HAL // synchronously from a different thread than the Handler thread. http://b/64771555. mHandler.post(() -> { @@ -340,7 +343,7 @@ public class OffloadController { } @Override - public void setAlert(long quotaBytes) { + public void onSetAlert(long quotaBytes) { // TODO: Ask offload HAL to notify alert without stopping traffic. } } diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java index 7e62e5aca9..1d100e6321 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java +++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java @@ -33,6 +33,8 @@ import static com.android.testutils.MiscAssertsKt.assertContainsAll; import static com.android.testutils.MiscAssertsKt.assertThrows; import static com.android.testutils.NetworkStatsUtilsKt.orderInsensitiveEquals; +import static junit.framework.Assert.assertNotNull; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; @@ -61,8 +63,7 @@ import android.net.LinkProperties; import android.net.NetworkStats; import android.net.NetworkStats.Entry; import android.net.RouteInfo; -import android.net.netstats.provider.AbstractNetworkStatsProvider; -import android.net.netstats.provider.NetworkStatsProviderCallback; +import android.net.netstats.provider.INetworkStatsProviderCallback; import android.net.util.SharedLog; import android.os.Handler; import android.os.Looper; @@ -108,12 +109,10 @@ public class OffloadControllerTest { @Mock private ApplicationInfo mApplicationInfo; @Mock private Context mContext; @Mock private NetworkStatsManager mStatsManager; - @Mock private NetworkStatsProviderCallback mTetherStatsProviderCb; + @Mock private INetworkStatsProviderCallback mTetherStatsProviderCb; + private OffloadController.OffloadTetheringStatsProvider mTetherStatsProvider; private final ArgumentCaptor mStringArrayCaptor = ArgumentCaptor.forClass(ArrayList.class); - private final ArgumentCaptor - mTetherStatsProviderCaptor = - ArgumentCaptor.forClass(OffloadController.OffloadTetheringStatsProvider.class); private final ArgumentCaptor mControlCallbackCaptor = ArgumentCaptor.forClass(OffloadHardwareInterface.ControlCallback.class); private MockContentResolver mContentResolver; @@ -126,8 +125,6 @@ public class OffloadControllerTest { mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); when(mContext.getContentResolver()).thenReturn(mContentResolver); FakeSettingsProvider.clearSettingsProvider(); - when(mStatsManager.registerNetworkStatsProvider(anyString(), any())) - .thenReturn(mTetherStatsProviderCb); } @After public void tearDown() throws Exception { @@ -154,8 +151,14 @@ public class OffloadControllerTest { private OffloadController makeOffloadController() throws Exception { OffloadController offload = new OffloadController(new Handler(Looper.getMainLooper()), mHardware, mContentResolver, mStatsManager, new SharedLog("test")); + final ArgumentCaptor + tetherStatsProviderCaptor = + ArgumentCaptor.forClass(OffloadController.OffloadTetheringStatsProvider.class); verify(mStatsManager).registerNetworkStatsProvider(anyString(), - mTetherStatsProviderCaptor.capture()); + tetherStatsProviderCaptor.capture()); + mTetherStatsProvider = tetherStatsProviderCaptor.getValue(); + assertNotNull(mTetherStatsProvider); + mTetherStatsProvider.setProviderCallbackBinder(mTetherStatsProviderCb); return offload; } @@ -413,9 +416,6 @@ public class OffloadControllerTest { final OffloadController offload = makeOffloadController(); offload.start(); - final OffloadController.OffloadTetheringStatsProvider provider = - mTetherStatsProviderCaptor.getValue(); - final String ethernetIface = "eth1"; final String mobileIface = "rmnet_data0"; @@ -443,8 +443,8 @@ public class OffloadControllerTest { inOrder.verify(mHardware, times(1)).getForwardedStats(eq(mobileIface)); // Verify that the fetched stats are stored. - final NetworkStats ifaceStats = provider.getTetherStats(STATS_PER_IFACE); - final NetworkStats uidStats = provider.getTetherStats(STATS_PER_UID); + final NetworkStats ifaceStats = mTetherStatsProvider.getTetherStats(STATS_PER_IFACE); + final NetworkStats uidStats = mTetherStatsProvider.getTetherStats(STATS_PER_UID); final NetworkStats expectedIfaceStats = new NetworkStats(0L, 2) .addValues(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999)) .addValues(buildTestEntry(STATS_PER_IFACE, ethernetIface, 12345, 54321)); @@ -462,13 +462,12 @@ public class OffloadControllerTest { NetworkStats.class); // Force pushing stats update to verify the stats reported. - provider.pushTetherStats(); - verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(), - ifaceStatsCaptor.capture(), uidStatsCaptor.capture()); + mTetherStatsProvider.pushTetherStats(); + verify(mTetherStatsProviderCb, times(1)) + .notifyStatsUpdated(anyInt(), ifaceStatsCaptor.capture(), uidStatsCaptor.capture()); assertTrue(orderInsensitiveEquals(expectedIfaceStats, ifaceStatsCaptor.getValue())); assertTrue(orderInsensitiveEquals(expectedUidStats, uidStatsCaptor.getValue())); - when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn( new ForwardedStats(100000, 100000)); offload.setUpstreamLinkProperties(null); @@ -483,8 +482,8 @@ public class OffloadControllerTest { inOrder.verifyNoMoreInteractions(); // Verify that the stored stats is accumulated. - final NetworkStats ifaceStatsAccu = provider.getTetherStats(STATS_PER_IFACE); - final NetworkStats uidStatsAccu = provider.getTetherStats(STATS_PER_UID); + final NetworkStats ifaceStatsAccu = mTetherStatsProvider.getTetherStats(STATS_PER_IFACE); + final NetworkStats uidStatsAccu = mTetherStatsProvider.getTetherStats(STATS_PER_UID); final NetworkStats expectedIfaceStatsAccu = new NetworkStats(0L, 2) .addValues(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999)) .addValues(buildTestEntry(STATS_PER_IFACE, ethernetIface, 112345, 154321)); @@ -498,7 +497,7 @@ public class OffloadControllerTest { // Verify that only diff of stats is reported. reset(mTetherStatsProviderCb); - provider.pushTetherStats(); + mTetherStatsProvider.pushTetherStats(); final NetworkStats expectedIfaceStatsDiff = new NetworkStats(0L, 2) .addValues(buildTestEntry(STATS_PER_IFACE, mobileIface, 0, 0)) .addValues(buildTestEntry(STATS_PER_IFACE, ethernetIface, 100000, 100000)); @@ -506,8 +505,8 @@ public class OffloadControllerTest { final NetworkStats expectedUidStatsDiff = new NetworkStats(0L, 2) .addValues(buildTestEntry(STATS_PER_UID, mobileIface, 0, 0)) .addValues(buildTestEntry(STATS_PER_UID, ethernetIface, 100000, 100000)); - verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(), - ifaceStatsCaptor.capture(), uidStatsCaptor.capture()); + verify(mTetherStatsProviderCb, times(1)) + .notifyStatsUpdated(anyInt(), ifaceStatsCaptor.capture(), uidStatsCaptor.capture()); assertTrue(orderInsensitiveEquals(expectedIfaceStatsDiff, ifaceStatsCaptor.getValue())); assertTrue(orderInsensitiveEquals(expectedUidStatsDiff, uidStatsCaptor.getValue())); } @@ -529,19 +528,18 @@ public class OffloadControllerTest { lp.setInterfaceName(ethernetIface); offload.setUpstreamLinkProperties(lp); - AbstractNetworkStatsProvider provider = mTetherStatsProviderCaptor.getValue(); final InOrder inOrder = inOrder(mHardware); when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(true); when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(true); // Applying an interface quota to the current upstream immediately sends it to the hardware. - provider.setLimit(ethernetIface, ethernetLimit); + mTetherStatsProvider.onSetLimit(ethernetIface, ethernetLimit); waitForIdle(); inOrder.verify(mHardware).setDataLimit(ethernetIface, ethernetLimit); inOrder.verifyNoMoreInteractions(); // Applying an interface quota to another upstream does not take any immediate action. - provider.setLimit(mobileIface, mobileLimit); + mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit); waitForIdle(); inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong()); @@ -554,7 +552,7 @@ public class OffloadControllerTest { // Setting a limit of ITetheringStatsProvider.QUOTA_UNLIMITED causes the limit to be set // to Long.MAX_VALUE. - provider.setLimit(mobileIface, ITetheringStatsProvider.QUOTA_UNLIMITED); + mTetherStatsProvider.onSetLimit(mobileIface, ITetheringStatsProvider.QUOTA_UNLIMITED); waitForIdle(); inOrder.verify(mHardware).setDataLimit(mobileIface, Long.MAX_VALUE); @@ -562,7 +560,7 @@ public class OffloadControllerTest { when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(false); lp.setInterfaceName(ethernetIface); offload.setUpstreamLinkProperties(lp); - provider.setLimit(mobileIface, mobileLimit); + mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit); waitForIdle(); inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong()); @@ -571,7 +569,7 @@ public class OffloadControllerTest { when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(false); lp.setInterfaceName(mobileIface); offload.setUpstreamLinkProperties(lp); - provider.setLimit(mobileIface, mobileLimit); + mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit); waitForIdle(); inOrder.verify(mHardware).getForwardedStats(ethernetIface); inOrder.verify(mHardware).stopOffloadControl(); @@ -587,7 +585,7 @@ public class OffloadControllerTest { OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue(); callback.onStoppedLimitReached(); - verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(), any(), any()); + verify(mTetherStatsProviderCb, times(1)).notifyStatsUpdated(anyInt(), any(), any()); } @Test @@ -691,7 +689,7 @@ public class OffloadControllerTest { verify(mHardware, times(1)).getForwardedStats(eq(RMNET0)); verify(mHardware, times(1)).getForwardedStats(eq(WLAN0)); // TODO: verify the exact stats reported. - verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(), any(), any()); + verify(mTetherStatsProviderCb, times(1)).notifyStatsUpdated(anyInt(), any(), any()); verifyNoMoreInteractions(mTetherStatsProviderCb); verifyNoMoreInteractions(mHardware); } @@ -756,7 +754,7 @@ public class OffloadControllerTest { // Verify forwarded stats behaviour. verify(mHardware, times(1)).getForwardedStats(eq(RMNET0)); verify(mHardware, times(1)).getForwardedStats(eq(WLAN0)); - verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(), any(), any()); + verify(mTetherStatsProviderCb, times(1)).notifyStatsUpdated(anyInt(), any(), any()); verifyNoMoreInteractions(mTetherStatsProviderCb); // TODO: verify local prefixes and downstreams are also pushed to the HAL. From abb0596a0a586a5ba0e0ea9b64da5af44a9f5b00 Mon Sep 17 00:00:00 2001 From: junyulai Date: Mon, 16 Mar 2020 13:27:28 +0800 Subject: [PATCH 050/188] [SP25] Rename functions that add Entry conditionally Currently, in NetworkStats, there are many methods to manipulate the records. However, some methods are similar and ambiguous, such as addEntry, addValues, setValues, addIfaceValues, combineValues and combineAllValues. Thus, properly grouping and renaming methods are necessary. In this change, for methods that add one record conditionally, name them addEntry. addValues -> addEntry Test: atest FrameworksNetTests ImsPhoneCallTrackerTest TetheringTests Fix: 148895143 Change-Id: I9495a198cf247e6c79100f7ac1edcea370b071de --- .../tethering/OffloadController.java | 2 +- .../tethering/OffloadControllerTest.java | 24 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java b/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java index d43c5c682f..15cdb6ad7a 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java +++ b/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java @@ -284,7 +284,7 @@ public class OffloadController { final ForwardedStats value = kv.getValue(); final Entry entry = new Entry(kv.getKey(), uid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, value.rxBytes, 0L, value.txBytes, 0L, 0L); - stats = stats.addValues(entry); + stats = stats.addEntry(entry); } return stats; diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java index 1d100e6321..fe840864fb 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java +++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java @@ -446,12 +446,12 @@ public class OffloadControllerTest { final NetworkStats ifaceStats = mTetherStatsProvider.getTetherStats(STATS_PER_IFACE); final NetworkStats uidStats = mTetherStatsProvider.getTetherStats(STATS_PER_UID); final NetworkStats expectedIfaceStats = new NetworkStats(0L, 2) - .addValues(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999)) - .addValues(buildTestEntry(STATS_PER_IFACE, ethernetIface, 12345, 54321)); + .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999)) + .addEntry(buildTestEntry(STATS_PER_IFACE, ethernetIface, 12345, 54321)); final NetworkStats expectedUidStats = new NetworkStats(0L, 2) - .addValues(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999)) - .addValues(buildTestEntry(STATS_PER_UID, ethernetIface, 12345, 54321)); + .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999)) + .addEntry(buildTestEntry(STATS_PER_UID, ethernetIface, 12345, 54321)); assertTrue(orderInsensitiveEquals(expectedIfaceStats, ifaceStats)); assertTrue(orderInsensitiveEquals(expectedUidStats, uidStats)); @@ -485,12 +485,12 @@ public class OffloadControllerTest { final NetworkStats ifaceStatsAccu = mTetherStatsProvider.getTetherStats(STATS_PER_IFACE); final NetworkStats uidStatsAccu = mTetherStatsProvider.getTetherStats(STATS_PER_UID); final NetworkStats expectedIfaceStatsAccu = new NetworkStats(0L, 2) - .addValues(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999)) - .addValues(buildTestEntry(STATS_PER_IFACE, ethernetIface, 112345, 154321)); + .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999)) + .addEntry(buildTestEntry(STATS_PER_IFACE, ethernetIface, 112345, 154321)); final NetworkStats expectedUidStatsAccu = new NetworkStats(0L, 2) - .addValues(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999)) - .addValues(buildTestEntry(STATS_PER_UID, ethernetIface, 112345, 154321)); + .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999)) + .addEntry(buildTestEntry(STATS_PER_UID, ethernetIface, 112345, 154321)); assertTrue(orderInsensitiveEquals(expectedIfaceStatsAccu, ifaceStatsAccu)); assertTrue(orderInsensitiveEquals(expectedUidStatsAccu, uidStatsAccu)); @@ -499,12 +499,12 @@ public class OffloadControllerTest { reset(mTetherStatsProviderCb); mTetherStatsProvider.pushTetherStats(); final NetworkStats expectedIfaceStatsDiff = new NetworkStats(0L, 2) - .addValues(buildTestEntry(STATS_PER_IFACE, mobileIface, 0, 0)) - .addValues(buildTestEntry(STATS_PER_IFACE, ethernetIface, 100000, 100000)); + .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 0, 0)) + .addEntry(buildTestEntry(STATS_PER_IFACE, ethernetIface, 100000, 100000)); final NetworkStats expectedUidStatsDiff = new NetworkStats(0L, 2) - .addValues(buildTestEntry(STATS_PER_UID, mobileIface, 0, 0)) - .addValues(buildTestEntry(STATS_PER_UID, ethernetIface, 100000, 100000)); + .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 0, 0)) + .addEntry(buildTestEntry(STATS_PER_UID, ethernetIface, 100000, 100000)); verify(mTetherStatsProviderCb, times(1)) .notifyStatsUpdated(anyInt(), ifaceStatsCaptor.capture(), uidStatsCaptor.capture()); assertTrue(orderInsensitiveEquals(expectedIfaceStatsDiff, ifaceStatsCaptor.getValue())); From b8fecf4b32afa875cea181f770a6ae96e02aca97 Mon Sep 17 00:00:00 2001 From: Remi NGUYEN VAN Date: Wed, 18 Mar 2020 18:31:39 +0900 Subject: [PATCH 051/188] Remove usage of Context.NETWORK_STACK_SERVICE The NetworkStack.getService() API should be used instead. Bug: 151243982 Test: atest FrameworksNetTests TetheringTests Manual tethering test Change-Id: I7855090bffbe895c8349ad4903b8f2eb55515f0b --- .../server/connectivity/tethering/TetheringService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java b/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java index 020b32adcf..c5329d8d33 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java +++ b/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java @@ -33,6 +33,7 @@ import android.net.ITetheringConnector; import android.net.ITetheringEventCallback; import android.net.NetworkCapabilities; import android.net.NetworkRequest; +import android.net.NetworkStack; import android.net.TetheringRequestParcel; import android.net.dhcp.DhcpServerCallbacks; import android.net.dhcp.DhcpServingParamsParcel; @@ -364,8 +365,7 @@ public class TetheringService extends Service { IBinder connector; try { final long before = System.currentTimeMillis(); - while ((connector = (IBinder) mContext.getSystemService( - Context.NETWORK_STACK_SERVICE)) == null) { + while ((connector = NetworkStack.getService()) == null) { if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) { Log.wtf(TAG, "Timeout, fail to get INetworkStackConnector"); return null; From 49bdf5615ff063f2e0cfdc63ef491389dece36d6 Mon Sep 17 00:00:00 2001 From: Anton Hansson Date: Fri, 17 Jan 2020 19:03:34 +0000 Subject: [PATCH 052/188] Add individual API tracking files for modules This adds metalava api tracking generation to the module stub rules, to make sure we know exactly what API a particular module stub exports. Bug: 147768409 Test: m update-api Exempt-From-Owner-Approval: Approved in master Change-Id: Iaf2ef5b5751eb208d119ddbc74481239366fe581 Merged-In: Iaf2ef5b5751eb208d119ddbc74481239366fe581 --- Tethering/common/TetheringLib/api/current.txt | 1 + .../TetheringLib/api/module-lib-current.txt | 126 ++++++++++++++++++ .../TetheringLib/api/module-lib-removed.txt | 1 + Tethering/common/TetheringLib/api/removed.txt | 1 + .../TetheringLib/api/system-current.txt | 104 +++++++++++++++ .../TetheringLib/api/system-removed.txt | 1 + 6 files changed, 234 insertions(+) create mode 100644 Tethering/common/TetheringLib/api/current.txt create mode 100644 Tethering/common/TetheringLib/api/module-lib-current.txt create mode 100644 Tethering/common/TetheringLib/api/module-lib-removed.txt create mode 100644 Tethering/common/TetheringLib/api/removed.txt create mode 100644 Tethering/common/TetheringLib/api/system-current.txt create mode 100644 Tethering/common/TetheringLib/api/system-removed.txt diff --git a/Tethering/common/TetheringLib/api/current.txt b/Tethering/common/TetheringLib/api/current.txt new file mode 100644 index 0000000000..d802177e24 --- /dev/null +++ b/Tethering/common/TetheringLib/api/current.txt @@ -0,0 +1 @@ +// Signature format: 2.0 diff --git a/Tethering/common/TetheringLib/api/module-lib-current.txt b/Tethering/common/TetheringLib/api/module-lib-current.txt new file mode 100644 index 0000000000..e25d77dfc9 --- /dev/null +++ b/Tethering/common/TetheringLib/api/module-lib-current.txt @@ -0,0 +1,126 @@ +// Signature format: 2.0 +package android.net { + + public final class TetheredClient implements android.os.Parcelable { + ctor public TetheredClient(@NonNull android.net.MacAddress, @NonNull java.util.Collection, int); + method public int describeContents(); + method @NonNull public java.util.List getAddresses(); + method @NonNull public android.net.MacAddress getMacAddress(); + method public int getTetheringType(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + + public static final class TetheredClient.AddressInfo implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public android.net.LinkAddress getAddress(); + method @Nullable public String getHostname(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + + public final class TetheringConstants { + field public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType"; + field public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback"; + field public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType"; + field public static final String EXTRA_RUN_PROVISION = "extraRunProvision"; + field public static final String EXTRA_SET_ALARM = "extraSetAlarm"; + } + + public class TetheringManager { + ctor public TetheringManager(@NonNull android.content.Context, @NonNull java.util.function.Supplier); + method public int getLastTetherError(@NonNull String); + method @NonNull public String[] getTetherableBluetoothRegexs(); + method @NonNull public String[] getTetherableIfaces(); + method @NonNull public String[] getTetherableUsbRegexs(); + method @NonNull public String[] getTetherableWifiRegexs(); + method @NonNull public String[] getTetheredIfaces(); + method @NonNull public String[] getTetheringErroredIfaces(); + method public boolean isTetheringSupported(); + method public boolean isTetheringSupported(@NonNull String); + 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); + method public void requestLatestTetheringEntitlementResult(int, @NonNull android.os.ResultReceiver, boolean); + method @Deprecated public int setUsbTethering(boolean); + method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(@NonNull android.net.TetheringManager.TetheringRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback); + method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(int, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback); + 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 @Deprecated public int tether(@NonNull String); + method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback); + method @Deprecated public int untether(@NonNull String); + field public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED"; + field public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY"; + field public static final String EXTRA_ACTIVE_TETHER = "tetherArray"; + field public static final String EXTRA_AVAILABLE_TETHER = "availableArray"; + field public static final String EXTRA_ERRORED_TETHER = "erroredArray"; + field public static final int TETHERING_BLUETOOTH = 2; // 0x2 + field public static final int TETHERING_ETHERNET = 5; // 0x5 + field public static final int TETHERING_INVALID = -1; // 0xffffffff + field public static final int TETHERING_NCM = 4; // 0x4 + field public static final int TETHERING_USB = 1; // 0x1 + field public static final int TETHERING_WIFI = 0; // 0x0 + field public static final int TETHERING_WIFI_P2P = 3; // 0x3 + field public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; // 0xc + field public static final int TETHER_ERROR_DISABLE_NAT_ERROR = 9; // 0x9 + field public static final int TETHER_ERROR_ENABLE_NAT_ERROR = 8; // 0x8 + field public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; // 0xd + field public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; // 0xa + field public static final int TETHER_ERROR_MASTER_ERROR = 5; // 0x5 + field public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; // 0xf + field public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; // 0xe + field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0 + field public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb + field public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; // 0x2 + field public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; // 0x6 + field public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; // 0x4 + field public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; // 0x1 + field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3 + field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7 + field public static final int TETHER_HARDWARE_OFFLOAD_FAILED = 2; // 0x2 + field public static final int TETHER_HARDWARE_OFFLOAD_STARTED = 1; // 0x1 + field public static final int TETHER_HARDWARE_OFFLOAD_STOPPED = 0; // 0x0 + } + + public static interface TetheringManager.OnTetheringEntitlementResultListener { + method public void onTetheringEntitlementResult(int); + } + + public abstract static class TetheringManager.StartTetheringCallback { + ctor public TetheringManager.StartTetheringCallback(); + method public void onTetheringFailed(int); + method public void onTetheringStarted(); + } + + public abstract static class TetheringManager.TetheringEventCallback { + ctor public TetheringManager.TetheringEventCallback(); + method public void onClientsChanged(@NonNull java.util.Collection); + method public void onError(@NonNull String, int); + method public void onOffloadStatusChanged(int); + method @Deprecated public void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps); + method public void onTetherableInterfacesChanged(@NonNull java.util.List); + method public void onTetheredInterfacesChanged(@NonNull java.util.List); + method public void onTetheringSupported(boolean); + method public void onUpstreamChanged(@Nullable android.net.Network); + } + + @Deprecated public static class TetheringManager.TetheringInterfaceRegexps { + ctor @Deprecated public TetheringManager.TetheringInterfaceRegexps(@NonNull String[], @NonNull String[], @NonNull String[]); + method @Deprecated @NonNull public java.util.List getTetherableBluetoothRegexs(); + method @Deprecated @NonNull public java.util.List getTetherableUsbRegexs(); + method @Deprecated @NonNull public java.util.List getTetherableWifiRegexs(); + } + + public static class TetheringManager.TetheringRequest { + } + + public static class TetheringManager.TetheringRequest.Builder { + ctor public TetheringManager.TetheringRequest.Builder(int); + method @NonNull public android.net.TetheringManager.TetheringRequest build(); + method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean); + method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setSilentProvisioning(boolean); + method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder useStaticIpv4Addresses(@NonNull android.net.LinkAddress); + } + +} + diff --git a/Tethering/common/TetheringLib/api/module-lib-removed.txt b/Tethering/common/TetheringLib/api/module-lib-removed.txt new file mode 100644 index 0000000000..d802177e24 --- /dev/null +++ b/Tethering/common/TetheringLib/api/module-lib-removed.txt @@ -0,0 +1 @@ +// Signature format: 2.0 diff --git a/Tethering/common/TetheringLib/api/removed.txt b/Tethering/common/TetheringLib/api/removed.txt new file mode 100644 index 0000000000..d802177e24 --- /dev/null +++ b/Tethering/common/TetheringLib/api/removed.txt @@ -0,0 +1 @@ +// Signature format: 2.0 diff --git a/Tethering/common/TetheringLib/api/system-current.txt b/Tethering/common/TetheringLib/api/system-current.txt new file mode 100644 index 0000000000..d6fcb625bd --- /dev/null +++ b/Tethering/common/TetheringLib/api/system-current.txt @@ -0,0 +1,104 @@ +// Signature format: 2.0 +package android.net { + + public final class TetheredClient implements android.os.Parcelable { + ctor public TetheredClient(@NonNull android.net.MacAddress, @NonNull java.util.Collection, int); + method public int describeContents(); + method @NonNull public java.util.List getAddresses(); + method @NonNull public android.net.MacAddress getMacAddress(); + method public int getTetheringType(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + + public static final class TetheredClient.AddressInfo implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public android.net.LinkAddress getAddress(); + method @Nullable public String getHostname(); + 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); + method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(@NonNull android.net.TetheringManager.TetheringRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback); + method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(int, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback); + 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 public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY"; + field public static final String EXTRA_ACTIVE_TETHER = "tetherArray"; + field public static final String EXTRA_AVAILABLE_TETHER = "availableArray"; + field public static final String EXTRA_ERRORED_TETHER = "erroredArray"; + field public static final int TETHERING_BLUETOOTH = 2; // 0x2 + field public static final int TETHERING_ETHERNET = 5; // 0x5 + field public static final int TETHERING_INVALID = -1; // 0xffffffff + field public static final int TETHERING_NCM = 4; // 0x4 + field public static final int TETHERING_USB = 1; // 0x1 + field public static final int TETHERING_WIFI = 0; // 0x0 + field public static final int TETHERING_WIFI_P2P = 3; // 0x3 + field public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; // 0xc + field public static final int TETHER_ERROR_DISABLE_NAT_ERROR = 9; // 0x9 + field public static final int TETHER_ERROR_ENABLE_NAT_ERROR = 8; // 0x8 + field public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; // 0xd + field public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; // 0xa + field public static final int TETHER_ERROR_MASTER_ERROR = 5; // 0x5 + field public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; // 0xf + field public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; // 0xe + field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0 + field public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb + field public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; // 0x2 + field public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; // 0x6 + field public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; // 0x4 + field public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; // 0x1 + field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3 + field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7 + field public static final int TETHER_HARDWARE_OFFLOAD_FAILED = 2; // 0x2 + field public static final int TETHER_HARDWARE_OFFLOAD_STARTED = 1; // 0x1 + field public static final int TETHER_HARDWARE_OFFLOAD_STOPPED = 0; // 0x0 + } + + public static interface TetheringManager.OnTetheringEntitlementResultListener { + method public void onTetheringEntitlementResult(int); + } + + public abstract static class TetheringManager.StartTetheringCallback { + ctor public TetheringManager.StartTetheringCallback(); + method public void onTetheringFailed(int); + method public void onTetheringStarted(); + } + + public abstract static class TetheringManager.TetheringEventCallback { + ctor public TetheringManager.TetheringEventCallback(); + method public void onClientsChanged(@NonNull java.util.Collection); + method public void onError(@NonNull String, int); + method public void onOffloadStatusChanged(int); + method @Deprecated public void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps); + method public void onTetherableInterfacesChanged(@NonNull java.util.List); + method public void onTetheredInterfacesChanged(@NonNull java.util.List); + method public void onTetheringSupported(boolean); + method public void onUpstreamChanged(@Nullable android.net.Network); + } + + @Deprecated public static class TetheringManager.TetheringInterfaceRegexps { + ctor @Deprecated public TetheringManager.TetheringInterfaceRegexps(@NonNull String[], @NonNull String[], @NonNull String[]); + method @Deprecated @NonNull public java.util.List getTetherableBluetoothRegexs(); + method @Deprecated @NonNull public java.util.List getTetherableUsbRegexs(); + method @Deprecated @NonNull public java.util.List getTetherableWifiRegexs(); + } + + public static class TetheringManager.TetheringRequest { + } + + public static class TetheringManager.TetheringRequest.Builder { + ctor public TetheringManager.TetheringRequest.Builder(int); + method @NonNull public android.net.TetheringManager.TetheringRequest build(); + method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean); + method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setSilentProvisioning(boolean); + method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder useStaticIpv4Addresses(@NonNull android.net.LinkAddress); + } + +} + diff --git a/Tethering/common/TetheringLib/api/system-removed.txt b/Tethering/common/TetheringLib/api/system-removed.txt new file mode 100644 index 0000000000..d802177e24 --- /dev/null +++ b/Tethering/common/TetheringLib/api/system-removed.txt @@ -0,0 +1 @@ +// Signature format: 2.0 From a5428687e4f20e25a31a3a38557f2e874689b58d Mon Sep 17 00:00:00 2001 From: Anton Hansson Date: Fri, 17 Jan 2020 19:03:34 +0000 Subject: [PATCH 053/188] Add individual API tracking files for modules This adds metalava api tracking generation to the module stub rules, to make sure we know exactly what API a particular module stub exports. Bug: 147768409 Test: m update-api Exempt-From-Owner-Approval: Approved in master Change-Id: Iaf2ef5b5751eb208d119ddbc74481239366fe581 Merged-In: Iaf2ef5b5751eb208d119ddbc74481239366fe581 (cherry picked from commit b602b0b2f18d96866a5d7e5d27958af774d1f802) --- Tethering/common/TetheringLib/api/current.txt | 1 + .../TetheringLib/api/module-lib-current.txt | 128 ++++++++++++++++++ .../TetheringLib/api/module-lib-removed.txt | 1 + Tethering/common/TetheringLib/api/removed.txt | 1 + .../TetheringLib/api/system-current.txt | 106 +++++++++++++++ .../TetheringLib/api/system-removed.txt | 1 + 6 files changed, 238 insertions(+) create mode 100644 Tethering/common/TetheringLib/api/current.txt create mode 100644 Tethering/common/TetheringLib/api/module-lib-current.txt create mode 100644 Tethering/common/TetheringLib/api/module-lib-removed.txt create mode 100644 Tethering/common/TetheringLib/api/removed.txt create mode 100644 Tethering/common/TetheringLib/api/system-current.txt create mode 100644 Tethering/common/TetheringLib/api/system-removed.txt diff --git a/Tethering/common/TetheringLib/api/current.txt b/Tethering/common/TetheringLib/api/current.txt new file mode 100644 index 0000000000..d802177e24 --- /dev/null +++ b/Tethering/common/TetheringLib/api/current.txt @@ -0,0 +1 @@ +// Signature format: 2.0 diff --git a/Tethering/common/TetheringLib/api/module-lib-current.txt b/Tethering/common/TetheringLib/api/module-lib-current.txt new file mode 100644 index 0000000000..8a7e975051 --- /dev/null +++ b/Tethering/common/TetheringLib/api/module-lib-current.txt @@ -0,0 +1,128 @@ +// Signature format: 2.0 +package android.net { + + public final class TetheredClient implements android.os.Parcelable { + ctor public TetheredClient(@NonNull android.net.MacAddress, @NonNull java.util.Collection, int); + method public int describeContents(); + method @NonNull public java.util.List getAddresses(); + method @NonNull public android.net.MacAddress getMacAddress(); + method public int getTetheringType(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + + public static final class TetheredClient.AddressInfo implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public android.net.LinkAddress getAddress(); + method @Nullable public String getHostname(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + + public final class TetheringConstants { + field public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType"; + field public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback"; + field public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType"; + field public static final String EXTRA_RUN_PROVISION = "extraRunProvision"; + field public static final String EXTRA_SET_ALARM = "extraSetAlarm"; + } + + public class TetheringManager { + ctor public TetheringManager(@NonNull android.content.Context, @NonNull java.util.function.Supplier); + method public int getLastTetherError(@NonNull String); + method @NonNull public String[] getTetherableBluetoothRegexs(); + method @NonNull public String[] getTetherableIfaces(); + method @NonNull public String[] getTetherableUsbRegexs(); + method @NonNull public String[] getTetherableWifiRegexs(); + method @NonNull public String[] getTetheredIfaces(); + method @NonNull public String[] getTetheringErroredIfaces(); + method public boolean isTetheringSupported(); + method public boolean isTetheringSupported(@NonNull String); + 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); + method public void requestLatestTetheringEntitlementResult(int, @NonNull android.os.ResultReceiver, boolean); + method @Deprecated public int setUsbTethering(boolean); + method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(@NonNull android.net.TetheringManager.TetheringRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback); + method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(int, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback); + 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 @Deprecated public int tether(@NonNull String); + method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback); + method @Deprecated public int untether(@NonNull String); + field public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED"; + field public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY"; + field public static final String EXTRA_ACTIVE_TETHER = "tetherArray"; + field public static final String EXTRA_AVAILABLE_TETHER = "availableArray"; + field public static final String EXTRA_ERRORED_TETHER = "erroredArray"; + field public static final int TETHERING_BLUETOOTH = 2; // 0x2 + field public static final int TETHERING_ETHERNET = 5; // 0x5 + field public static final int TETHERING_INVALID = -1; // 0xffffffff + field public static final int TETHERING_NCM = 4; // 0x4 + field public static final int TETHERING_USB = 1; // 0x1 + field public static final int TETHERING_WIFI = 0; // 0x0 + field public static final int TETHERING_WIFI_P2P = 3; // 0x3 + field public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; // 0xc + field public static final int TETHER_ERROR_DISABLE_NAT_ERROR = 9; // 0x9 + field public static final int TETHER_ERROR_ENABLE_NAT_ERROR = 8; // 0x8 + field public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; // 0xd + field public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; // 0xa + field public static final int TETHER_ERROR_MASTER_ERROR = 5; // 0x5 + field public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; // 0xf + field public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; // 0xe + field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0 + field public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb + field public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; // 0x2 + field public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; // 0x6 + field public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; // 0x4 + field public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; // 0x1 + field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3 + field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7 + field public static final int TETHER_HARDWARE_OFFLOAD_FAILED = 2; // 0x2 + field public static final int TETHER_HARDWARE_OFFLOAD_STARTED = 1; // 0x1 + field public static final int TETHER_HARDWARE_OFFLOAD_STOPPED = 0; // 0x0 + } + + public static interface TetheringManager.OnTetheringEntitlementResultListener { + method public void onTetheringEntitlementResult(int); + } + + public abstract static class TetheringManager.StartTetheringCallback { + ctor public TetheringManager.StartTetheringCallback(); + method public void onTetheringFailed(int); + method public void onTetheringStarted(); + } + + public abstract static class TetheringManager.TetheringEventCallback { + ctor public TetheringManager.TetheringEventCallback(); + method public void onClientsChanged(@NonNull java.util.Collection); + method public void onError(@NonNull String, int); + method public void onOffloadStatusChanged(int); + method @Deprecated public void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps); + method public void onTetherableInterfacesChanged(@NonNull java.util.List); + method public void onTetheredInterfacesChanged(@NonNull java.util.List); + method public void onTetheringSupported(boolean); + method public void onUpstreamChanged(@Nullable android.net.Network); + } + + @Deprecated public static class TetheringManager.TetheringInterfaceRegexps { + ctor @Deprecated public TetheringManager.TetheringInterfaceRegexps(@NonNull String[], @NonNull String[], @NonNull String[]); + method @Deprecated @NonNull public java.util.List getTetherableBluetoothRegexs(); + method @Deprecated @NonNull public java.util.List getTetherableUsbRegexs(); + method @Deprecated @NonNull public java.util.List getTetherableWifiRegexs(); + } + + public static class TetheringManager.TetheringRequest { + } + + public static class TetheringManager.TetheringRequest.Builder { + ctor public TetheringManager.TetheringRequest.Builder(int); + method @NonNull public android.net.TetheringManager.TetheringRequest build(); + method @Nullable public android.net.LinkAddress getClientStaticIpv4Address(); + method @Nullable public android.net.LinkAddress getLocalIpv4Address(); + method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean); + method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setSilentProvisioning(boolean); + method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setStaticIpv4Addresses(@NonNull android.net.LinkAddress, @NonNull android.net.LinkAddress); + } + +} + diff --git a/Tethering/common/TetheringLib/api/module-lib-removed.txt b/Tethering/common/TetheringLib/api/module-lib-removed.txt new file mode 100644 index 0000000000..d802177e24 --- /dev/null +++ b/Tethering/common/TetheringLib/api/module-lib-removed.txt @@ -0,0 +1 @@ +// Signature format: 2.0 diff --git a/Tethering/common/TetheringLib/api/removed.txt b/Tethering/common/TetheringLib/api/removed.txt new file mode 100644 index 0000000000..d802177e24 --- /dev/null +++ b/Tethering/common/TetheringLib/api/removed.txt @@ -0,0 +1 @@ +// Signature format: 2.0 diff --git a/Tethering/common/TetheringLib/api/system-current.txt b/Tethering/common/TetheringLib/api/system-current.txt new file mode 100644 index 0000000000..ac739532a4 --- /dev/null +++ b/Tethering/common/TetheringLib/api/system-current.txt @@ -0,0 +1,106 @@ +// Signature format: 2.0 +package android.net { + + public final class TetheredClient implements android.os.Parcelable { + ctor public TetheredClient(@NonNull android.net.MacAddress, @NonNull java.util.Collection, int); + method public int describeContents(); + method @NonNull public java.util.List getAddresses(); + method @NonNull public android.net.MacAddress getMacAddress(); + method public int getTetheringType(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + + public static final class TetheredClient.AddressInfo implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public android.net.LinkAddress getAddress(); + method @Nullable public String getHostname(); + 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); + method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(@NonNull android.net.TetheringManager.TetheringRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback); + method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(int, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback); + 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 public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY"; + field public static final String EXTRA_ACTIVE_TETHER = "tetherArray"; + field public static final String EXTRA_AVAILABLE_TETHER = "availableArray"; + field public static final String EXTRA_ERRORED_TETHER = "erroredArray"; + field public static final int TETHERING_BLUETOOTH = 2; // 0x2 + field public static final int TETHERING_ETHERNET = 5; // 0x5 + field public static final int TETHERING_INVALID = -1; // 0xffffffff + field public static final int TETHERING_NCM = 4; // 0x4 + field public static final int TETHERING_USB = 1; // 0x1 + field public static final int TETHERING_WIFI = 0; // 0x0 + field public static final int TETHERING_WIFI_P2P = 3; // 0x3 + field public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; // 0xc + field public static final int TETHER_ERROR_DISABLE_NAT_ERROR = 9; // 0x9 + field public static final int TETHER_ERROR_ENABLE_NAT_ERROR = 8; // 0x8 + field public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; // 0xd + field public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; // 0xa + field public static final int TETHER_ERROR_MASTER_ERROR = 5; // 0x5 + field public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; // 0xf + field public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; // 0xe + field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0 + field public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb + field public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; // 0x2 + field public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; // 0x6 + field public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; // 0x4 + field public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; // 0x1 + field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3 + field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7 + field public static final int TETHER_HARDWARE_OFFLOAD_FAILED = 2; // 0x2 + field public static final int TETHER_HARDWARE_OFFLOAD_STARTED = 1; // 0x1 + field public static final int TETHER_HARDWARE_OFFLOAD_STOPPED = 0; // 0x0 + } + + public static interface TetheringManager.OnTetheringEntitlementResultListener { + method public void onTetheringEntitlementResult(int); + } + + public abstract static class TetheringManager.StartTetheringCallback { + ctor public TetheringManager.StartTetheringCallback(); + method public void onTetheringFailed(int); + method public void onTetheringStarted(); + } + + public abstract static class TetheringManager.TetheringEventCallback { + ctor public TetheringManager.TetheringEventCallback(); + method public void onClientsChanged(@NonNull java.util.Collection); + method public void onError(@NonNull String, int); + method public void onOffloadStatusChanged(int); + method @Deprecated public void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps); + method public void onTetherableInterfacesChanged(@NonNull java.util.List); + method public void onTetheredInterfacesChanged(@NonNull java.util.List); + method public void onTetheringSupported(boolean); + method public void onUpstreamChanged(@Nullable android.net.Network); + } + + @Deprecated public static class TetheringManager.TetheringInterfaceRegexps { + ctor @Deprecated public TetheringManager.TetheringInterfaceRegexps(@NonNull String[], @NonNull String[], @NonNull String[]); + method @Deprecated @NonNull public java.util.List getTetherableBluetoothRegexs(); + method @Deprecated @NonNull public java.util.List getTetherableUsbRegexs(); + method @Deprecated @NonNull public java.util.List getTetherableWifiRegexs(); + } + + public static class TetheringManager.TetheringRequest { + } + + public static class TetheringManager.TetheringRequest.Builder { + ctor public TetheringManager.TetheringRequest.Builder(int); + method @NonNull public android.net.TetheringManager.TetheringRequest build(); + method @Nullable public android.net.LinkAddress getClientStaticIpv4Address(); + method @Nullable public android.net.LinkAddress getLocalIpv4Address(); + method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean); + method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setSilentProvisioning(boolean); + method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setStaticIpv4Addresses(@NonNull android.net.LinkAddress, @NonNull android.net.LinkAddress); + } + +} + diff --git a/Tethering/common/TetheringLib/api/system-removed.txt b/Tethering/common/TetheringLib/api/system-removed.txt new file mode 100644 index 0000000000..d802177e24 --- /dev/null +++ b/Tethering/common/TetheringLib/api/system-removed.txt @@ -0,0 +1 @@ +// Signature format: 2.0 From 57e22c5ab9bc49c051bc96f904d0658764ed9110 Mon Sep 17 00:00:00 2001 From: Paul Hu Date: Thu, 19 Mar 2020 02:50:15 +0000 Subject: [PATCH 054/188] [TNU1.1]Add tethering notification strings Add string for no upstream and cellular roaming notification. Bug: 145629001 Bug: 147818698 Test: atest TetheringTests Change-Id: I30f68d83344f66fb3ef77abf3f8748c3eb1276f0 Merged-In: I30f68d83344f66fb3ef77abf3f8748c3eb1276f0 (cherry picked from aosp/1260112) --- Tethering/res/values-mcc204-mnc04/strings.xml | 26 +++++++++++++++++++ .../res/values-mcc310-mnc004/strings.xml | 26 +++++++++++++++++++ .../res/values-mcc311-mnc480/strings.xml | 26 +++++++++++++++++++ Tethering/res/values/strings.xml | 10 +++++++ 4 files changed, 88 insertions(+) create mode 100644 Tethering/res/values-mcc204-mnc04/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480/strings.xml diff --git a/Tethering/res/values-mcc204-mnc04/strings.xml b/Tethering/res/values-mcc204-mnc04/strings.xml new file mode 100644 index 0000000000..6bc2e2aa92 --- /dev/null +++ b/Tethering/res/values-mcc204-mnc04/strings.xml @@ -0,0 +1,26 @@ + + + + + Tethering & Mobile Hotspot has no internet access + + + Roaming + + You will be charged on a per MB basis for all data sent or received while using this service outside the Verizon Network. Please check our website for current international rates. To minimize charges, visit My Verizon periodically to monitor your usage, check your device settings to confirm which devices are connected, and consider using alternate data connections when available + + Continue + \ No newline at end of file diff --git a/Tethering/res/values-mcc310-mnc004/strings.xml b/Tethering/res/values-mcc310-mnc004/strings.xml new file mode 100644 index 0000000000..6bc2e2aa92 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004/strings.xml @@ -0,0 +1,26 @@ + + + + + Tethering & Mobile Hotspot has no internet access + + + Roaming + + You will be charged on a per MB basis for all data sent or received while using this service outside the Verizon Network. Please check our website for current international rates. To minimize charges, visit My Verizon periodically to monitor your usage, check your device settings to confirm which devices are connected, and consider using alternate data connections when available + + Continue + \ No newline at end of file diff --git a/Tethering/res/values-mcc311-mnc480/strings.xml b/Tethering/res/values-mcc311-mnc480/strings.xml new file mode 100644 index 0000000000..6bc2e2aa92 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480/strings.xml @@ -0,0 +1,26 @@ + + + + + Tethering & Mobile Hotspot has no internet access + + + Roaming + + You will be charged on a per MB basis for all data sent or received while using this service outside the Verizon Network. Please check our website for current international rates. To minimize charges, visit My Verizon periodically to monitor your usage, check your device settings to confirm which devices are connected, and consider using alternate data connections when available + + Continue + \ No newline at end of file diff --git a/Tethering/res/values/strings.xml b/Tethering/res/values/strings.xml index ba98a66ff7..d50884b2a7 100644 --- a/Tethering/res/values/strings.xml +++ b/Tethering/res/values/strings.xml @@ -32,4 +32,14 @@ Internet" settings page. That is currently the tether_settings_title_all string. --> Hotspot & tethering status + + + + + + + + + + \ No newline at end of file From 9462a3c9f0f9d65f45c63470ad8f4ca4fcab5750 Mon Sep 17 00:00:00 2001 From: Automerger Merge Worker Date: Tue, 17 Mar 2020 16:59:57 +0000 Subject: [PATCH 055/188] Support static address configuration Application can specify static ipv4 server and client address to setup tethering and this is one shot configuration. Tethering service would not save the configuration and the configuration would be reset when tethering stop or start failure. When startTethering callback fired, it just mean tethering is requested successful. Therefore, callers may call startTethering again if startTethering successful but do not receive following tethering active notification for a while. Tethering service never actually does anything synchronously when startTethering is called: -startProvisioningIfNeeded just posts a message to the handler thread. -enableTetheringInternal doesn't do anything synchronously, it just asks the downstreams to get their interfaces ready and waits for callbacks. If tethering is already enabled with a different request, tethering would be disabled and re-enabled. Bug: 141256482 Test: -build, flash, boot -atest TetheringTests -atest CtsTetheringTest Change-Id: I2b2dd965a673e6f1626738d41b5d443f0f9fbd0e Merged-In: I0399917e7cefa1547d617e688225544c4fc1a231 (cherry picked from commit 5d6723e24e21154bef3967585a8adc069e007f49) --- .../TetheringLib/api/module-lib-current.txt | 4 +- .../TetheringLib/api/system-current.txt | 4 +- .../src/android/net/TetheringManager.java | 36 +++++- .../android/net/TetheringRequestParcel.aidl | 1 + Tethering/src/android/net/ip/IpServer.java | 28 ++++- .../src/android/net/util/TetheringUtils.java | 16 +++ .../connectivity/tethering/Tethering.java | 73 ++++++++---- .../android/net/util/TetheringUtilsTest.java | 87 ++++++++++++++ .../connectivity/tethering/TetheringTest.java | 107 ++++++++++++++++-- 9 files changed, 316 insertions(+), 40 deletions(-) create mode 100644 Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java diff --git a/Tethering/common/TetheringLib/api/module-lib-current.txt b/Tethering/common/TetheringLib/api/module-lib-current.txt index e25d77dfc9..8a7e975051 100644 --- a/Tethering/common/TetheringLib/api/module-lib-current.txt +++ b/Tethering/common/TetheringLib/api/module-lib-current.txt @@ -117,9 +117,11 @@ package android.net { public static class TetheringManager.TetheringRequest.Builder { ctor public TetheringManager.TetheringRequest.Builder(int); method @NonNull public android.net.TetheringManager.TetheringRequest build(); + method @Nullable public android.net.LinkAddress getClientStaticIpv4Address(); + method @Nullable public android.net.LinkAddress getLocalIpv4Address(); method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean); method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setSilentProvisioning(boolean); - method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder useStaticIpv4Addresses(@NonNull android.net.LinkAddress); + method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setStaticIpv4Addresses(@NonNull android.net.LinkAddress, @NonNull android.net.LinkAddress); } } diff --git a/Tethering/common/TetheringLib/api/system-current.txt b/Tethering/common/TetheringLib/api/system-current.txt index d6fcb625bd..ac739532a4 100644 --- a/Tethering/common/TetheringLib/api/system-current.txt +++ b/Tethering/common/TetheringLib/api/system-current.txt @@ -95,9 +95,11 @@ package android.net { public static class TetheringManager.TetheringRequest.Builder { ctor public TetheringManager.TetheringRequest.Builder(int); method @NonNull public android.net.TetheringManager.TetheringRequest build(); + method @Nullable public android.net.LinkAddress getClientStaticIpv4Address(); + method @Nullable public android.net.LinkAddress getLocalIpv4Address(); method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean); method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setSilentProvisioning(boolean); - method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder useStaticIpv4Addresses(@NonNull android.net.LinkAddress); + method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setStaticIpv4Addresses(@NonNull android.net.LinkAddress, @NonNull android.net.LinkAddress); } } diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java index 7f831ced7b..f2045df947 100644 --- a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java +++ b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java @@ -512,19 +512,36 @@ public class TetheringManager { mBuilderParcel = new TetheringRequestParcel(); mBuilderParcel.tetheringType = type; mBuilderParcel.localIPv4Address = null; + mBuilderParcel.staticClientAddress = null; mBuilderParcel.exemptFromEntitlementCheck = false; mBuilderParcel.showProvisioningUi = true; } /** - * Configure tethering with static IPv4 assignment (with DHCP disabled). + * Configure tethering with static IPv4 assignment. * - * @param localIPv4Address The preferred local IPv4 address to use. + * The clientAddress must be in the localIPv4Address prefix. A DHCP server will be + * started, but will only be able to offer the client address. The two addresses must + * be in the same prefix. + * + * @param localIPv4Address The preferred local IPv4 link address to use. + * @param clientAddress The static client address. */ @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) @NonNull - public Builder useStaticIpv4Addresses(@NonNull final LinkAddress localIPv4Address) { + public Builder setStaticIpv4Addresses(@NonNull final LinkAddress localIPv4Address, + @NonNull final LinkAddress clientAddress) { + Objects.requireNonNull(localIPv4Address); + Objects.requireNonNull(clientAddress); + if (localIPv4Address.getPrefixLength() != clientAddress.getPrefixLength() + || !localIPv4Address.isIpv4() || !clientAddress.isIpv4() + || !new IpPrefix(localIPv4Address.toString()).equals( + new IpPrefix(clientAddress.toString()))) { + throw new IllegalArgumentException("Invalid server or client addresses"); + } + mBuilderParcel.localIPv4Address = localIPv4Address; + mBuilderParcel.staticClientAddress = clientAddress; return this; } @@ -549,6 +566,18 @@ public class TetheringManager { public TetheringRequest build() { return new TetheringRequest(mBuilderParcel); } + + /** Get static server address. */ + @Nullable + public LinkAddress getLocalIpv4Address() { + return mBuilderParcel.localIPv4Address; + } + + /** Get static client address. */ + @Nullable + public LinkAddress getClientStaticIpv4Address() { + return mBuilderParcel.staticClientAddress; + } } /** @@ -563,6 +592,7 @@ public class TetheringManager { public String toString() { return "TetheringRequest [ type= " + mRequestParcel.tetheringType + ", localIPv4Address= " + mRequestParcel.localIPv4Address + + ", staticClientAddress= " + mRequestParcel.staticClientAddress + ", exemptFromEntitlementCheck= " + mRequestParcel.exemptFromEntitlementCheck + ", showProvisioningUi= " + mRequestParcel.showProvisioningUi + " ]"; diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl b/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl index bf19d85f6a..c0280d3dbf 100644 --- a/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl +++ b/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl @@ -25,6 +25,7 @@ import android.net.LinkAddress; parcelable TetheringRequestParcel { int tetheringType; LinkAddress localIPv4Address; + LinkAddress staticClientAddress; boolean exemptFromEntitlementCheck; boolean showProvisioningUi; } diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java index 6c0c432d46..433b903ebe 100644 --- a/Tethering/src/android/net/ip/IpServer.java +++ b/Tethering/src/android/net/ip/IpServer.java @@ -35,6 +35,7 @@ import android.net.MacAddress; import android.net.RouteInfo; import android.net.TetheredClient; import android.net.TetheringManager; +import android.net.TetheringRequestParcel; import android.net.dhcp.DhcpLeaseParcelable; import android.net.dhcp.DhcpServerCallbacks; import android.net.dhcp.DhcpServingParamsParcel; @@ -243,6 +244,10 @@ public class IpServer extends StateMachine { private IDhcpServer mDhcpServer; private RaParams mLastRaParams; private LinkAddress mIpv4Address; + + private LinkAddress mStaticIpv4ServerAddr; + private LinkAddress mStaticIpv4ClientAddr; + @NonNull private List mDhcpLeases = Collections.emptyList(); @@ -547,6 +552,8 @@ public class IpServer extends StateMachine { // into calls to InterfaceController, shared with startIPv4(). mInterfaceCtrl.clearIPv4Address(); mIpv4Address = null; + mStaticIpv4ServerAddr = null; + mStaticIpv4ClientAddr = null; } private boolean configureIPv4(boolean enabled) { @@ -557,7 +564,10 @@ public class IpServer extends StateMachine { final Inet4Address srvAddr; int prefixLen = 0; try { - if (mInterfaceType == TetheringManager.TETHERING_USB + if (mStaticIpv4ServerAddr != null) { + srvAddr = (Inet4Address) mStaticIpv4ServerAddr.getAddress(); + prefixLen = mStaticIpv4ServerAddr.getPrefixLength(); + } else if (mInterfaceType == TetheringManager.TETHERING_USB || mInterfaceType == TetheringManager.TETHERING_NCM) { srvAddr = (Inet4Address) parseNumericAddress(USB_NEAR_IFACE_ADDR); prefixLen = USB_PREFIX_LENGTH; @@ -602,10 +612,6 @@ public class IpServer extends StateMachine { return false; } - if (!configureDhcp(enabled, srvAddr, prefixLen)) { - return false; - } - // Directly-connected route. final IpPrefix ipv4Prefix = new IpPrefix(mIpv4Address.getAddress(), mIpv4Address.getPrefixLength()); @@ -617,7 +623,8 @@ public class IpServer extends StateMachine { mLinkProperties.removeLinkAddress(mIpv4Address); mLinkProperties.removeRoute(route); } - return true; + + return configureDhcp(enabled, srvAddr, prefixLen); } private String getRandomWifiIPv4Address() { @@ -937,6 +944,13 @@ public class IpServer extends StateMachine { mLinkProperties.setInterfaceName(mIfaceName); } + private void maybeConfigureStaticIp(final TetheringRequestParcel request) { + if (request == null) return; + + mStaticIpv4ServerAddr = request.localIPv4Address; + mStaticIpv4ClientAddr = request.staticClientAddress; + } + class InitialState extends State { @Override public void enter() { @@ -951,9 +965,11 @@ public class IpServer extends StateMachine { mLastError = TetheringManager.TETHER_ERROR_NO_ERROR; switch (message.arg1) { case STATE_LOCAL_ONLY: + maybeConfigureStaticIp((TetheringRequestParcel) message.obj); transitionTo(mLocalHotspotState); break; case STATE_TETHERED: + maybeConfigureStaticIp((TetheringRequestParcel) message.obj); transitionTo(mTetheredState); break; default: diff --git a/Tethering/src/android/net/util/TetheringUtils.java b/Tethering/src/android/net/util/TetheringUtils.java index 5a6d5c1cbf..dd67dddae1 100644 --- a/Tethering/src/android/net/util/TetheringUtils.java +++ b/Tethering/src/android/net/util/TetheringUtils.java @@ -15,8 +15,11 @@ */ package android.net.util; +import android.net.TetheringRequestParcel; + import java.io.FileDescriptor; import java.net.SocketException; +import java.util.Objects; /** * Native methods for tethering utilization. @@ -38,4 +41,17 @@ public class TetheringUtils { public static int uint16(short s) { return s & 0xffff; } + + /** Check whether two TetheringRequestParcels are the same. */ + public static boolean isTetheringRequestEquals(final TetheringRequestParcel request, + final TetheringRequestParcel otherRequest) { + if (request == otherRequest) return true; + + return request != null && otherRequest != null + && request.tetheringType == otherRequest.tetheringType + && Objects.equals(request.localIPv4Address, otherRequest.localIPv4Address) + && Objects.equals(request.staticClientAddress, otherRequest.staticClientAddress) + && request.exemptFromEntitlementCheck == otherRequest.exemptFromEntitlementCheck + && request.showProvisioningUi == otherRequest.showProvisioningUi; + } } diff --git a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java index 3d8dbab7d4..6e918929f8 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java +++ b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java @@ -92,6 +92,7 @@ import android.net.util.BaseNetdUnsolicitedEventListener; import android.net.util.InterfaceSet; import android.net.util.PrefixUtils; import android.net.util.SharedLog; +import android.net.util.TetheringUtils; import android.net.util.VersionedBroadcastListener; import android.net.wifi.WifiClient; import android.net.wifi.WifiManager; @@ -196,6 +197,11 @@ public class Tethering { private final SharedLog mLog = new SharedLog(TAG); private final RemoteCallbackList mTetheringEventCallbacks = new RemoteCallbackList<>(); + // Currently active tethering requests per tethering type. Only one of each type can be + // requested at a time. After a tethering type is requested, the map keeps tethering parameters + // to be used after the interface comes up asynchronously. + private final SparseArray mActiveTetheringRequests = + new SparseArray<>(); // used to synchronize public access to members private final Object mPublicSync; @@ -487,14 +493,31 @@ public class Tethering { } void startTethering(final TetheringRequestParcel request, final IIntResultListener listener) { - mEntitlementMgr.startProvisioningIfNeeded(request.tetheringType, - request.showProvisioningUi); - enableTetheringInternal(request.tetheringType, true /* enabled */, listener); + mHandler.post(() -> { + final TetheringRequestParcel unfinishedRequest = mActiveTetheringRequests.get( + request.tetheringType); + // If tethering is already enabled with a different request, + // disable before re-enabling. + if (unfinishedRequest != null + && !TetheringUtils.isTetheringRequestEquals(unfinishedRequest, request)) { + enableTetheringInternal(request.tetheringType, false /* disabled */, null); + mEntitlementMgr.stopProvisioningIfNeeded(request.tetheringType); + } + mActiveTetheringRequests.put(request.tetheringType, request); + + mEntitlementMgr.startProvisioningIfNeeded(request.tetheringType, + request.showProvisioningUi); + enableTetheringInternal(request.tetheringType, true /* enabled */, listener); + }); } void stopTethering(int type) { - enableTetheringInternal(type, false /* disabled */, null); - mEntitlementMgr.stopProvisioningIfNeeded(type); + mHandler.post(() -> { + mActiveTetheringRequests.remove(type); + + enableTetheringInternal(type, false /* disabled */, null); + mEntitlementMgr.stopProvisioningIfNeeded(type); + }); } /** @@ -503,39 +526,45 @@ public class Tethering { */ private void enableTetheringInternal(int type, boolean enable, final IIntResultListener listener) { - int result; + int result = TETHER_ERROR_NO_ERROR; switch (type) { case TETHERING_WIFI: result = setWifiTethering(enable); - sendTetherResult(listener, result); break; case TETHERING_USB: result = setUsbTethering(enable); - sendTetherResult(listener, result); break; case TETHERING_BLUETOOTH: setBluetoothTethering(enable, listener); break; case TETHERING_NCM: result = setNcmTethering(enable); - sendTetherResult(listener, result); break; case TETHERING_ETHERNET: result = setEthernetTethering(enable); - sendTetherResult(listener, result); break; default: Log.w(TAG, "Invalid tether type."); - sendTetherResult(listener, TETHER_ERROR_UNKNOWN_IFACE); + result = TETHER_ERROR_UNKNOWN_IFACE; + } + + // The result of Bluetooth tethering will be sent by #setBluetoothTethering. + if (type != TETHERING_BLUETOOTH) { + sendTetherResult(listener, result, type); } } - private void sendTetherResult(final IIntResultListener listener, int result) { + private void sendTetherResult(final IIntResultListener listener, final int result, + final int type) { if (listener != null) { try { listener.onResult(result); } catch (RemoteException e) { } } + + // If changing tethering fail, remove corresponding request + // no matter who trigger the start/stop. + if (result != TETHER_ERROR_NO_ERROR) mActiveTetheringRequests.remove(type); } private int setWifiTethering(final boolean enable) { @@ -565,7 +594,7 @@ public class Tethering { if (adapter == null || !adapter.isEnabled()) { Log.w(TAG, "Tried to enable bluetooth tethering with null or disabled adapter. null: " + (adapter == null)); - sendTetherResult(listener, TETHER_ERROR_SERVICE_UNAVAIL); + sendTetherResult(listener, TETHER_ERROR_SERVICE_UNAVAIL, TETHERING_BLUETOOTH); return; } @@ -594,7 +623,7 @@ public class Tethering { final int result = (((BluetoothPan) proxy).isTetheringOn() == enable) ? TETHER_ERROR_NO_ERROR : TETHER_ERROR_MASTER_ERROR; - sendTetherResult(listener, result); + sendTetherResult(listener, result, TETHERING_BLUETOOTH); adapter.closeProfileProxy(BluetoothProfile.PAN, proxy); } }, BluetoothProfile.PAN); @@ -672,12 +701,18 @@ public class Tethering { Log.e(TAG, "Tried to Tether an unavailable iface: " + iface + ", ignoring"); return TETHER_ERROR_UNAVAIL_IFACE; } - // NOTE: If a CMD_TETHER_REQUESTED message is already in the TISM's - // queue but not yet processed, this will be a no-op and it will not - // return an error. + // NOTE: If a CMD_TETHER_REQUESTED message is already in the TISM's queue but not yet + // processed, this will be a no-op and it will not return an error. // - // TODO: reexamine the threading and messaging model. - tetherState.ipServer.sendMessage(IpServer.CMD_TETHER_REQUESTED, requestedState); + // This code cannot race with untether() because they both synchronize on mPublicSync. + // TODO: reexamine the threading and messaging model to totally remove mPublicSync. + final int type = tetherState.ipServer.interfaceType(); + final TetheringRequestParcel request = mActiveTetheringRequests.get(type, null); + if (request != null) { + mActiveTetheringRequests.delete(type); + } + tetherState.ipServer.sendMessage(IpServer.CMD_TETHER_REQUESTED, requestedState, 0, + request); return TETHER_ERROR_NO_ERROR; } } diff --git a/Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java b/Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java new file mode 100644 index 0000000000..1499f3be22 --- /dev/null +++ b/Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java @@ -0,0 +1,87 @@ +/* + * 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.util; + +import static android.net.TetheringManager.TETHERING_USB; +import static android.net.TetheringManager.TETHERING_WIFI; + +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import android.net.LinkAddress; +import android.net.TetheringRequestParcel; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.testutils.MiscAssertsKt; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class TetheringUtilsTest { + private static final LinkAddress TEST_SERVER_ADDR = new LinkAddress("192.168.43.1/24"); + private static final LinkAddress TEST_CLIENT_ADDR = new LinkAddress("192.168.43.5/24"); + private TetheringRequestParcel mTetheringRequest; + + @Before + public void setUp() { + mTetheringRequest = makeTetheringRequestParcel(); + } + + public TetheringRequestParcel makeTetheringRequestParcel() { + final TetheringRequestParcel request = new TetheringRequestParcel(); + request.tetheringType = TETHERING_WIFI; + request.localIPv4Address = TEST_SERVER_ADDR; + request.staticClientAddress = TEST_CLIENT_ADDR; + request.exemptFromEntitlementCheck = false; + request.showProvisioningUi = true; + return request; + } + + @Test + public void testIsTetheringRequestEquals() throws Exception { + TetheringRequestParcel request = makeTetheringRequestParcel(); + + assertTrue(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, mTetheringRequest)); + assertTrue(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request)); + assertTrue(TetheringUtils.isTetheringRequestEquals(null, null)); + assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, null)); + assertFalse(TetheringUtils.isTetheringRequestEquals(null, mTetheringRequest)); + + request = makeTetheringRequestParcel(); + request.tetheringType = TETHERING_USB; + assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request)); + + request = makeTetheringRequestParcel(); + request.localIPv4Address = null; + request.staticClientAddress = null; + assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request)); + + request = makeTetheringRequestParcel(); + request.exemptFromEntitlementCheck = true; + assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request)); + + request = makeTetheringRequestParcel(); + request.showProvisioningUi = false; + assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request)); + + MiscAssertsKt.assertFieldCountEquals(5, TetheringRequestParcel.class); + } +} diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java index 820c852a27..60d7ad1c5b 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java @@ -82,6 +82,7 @@ import android.hardware.usb.UsbManager; import android.net.ConnectivityManager; import android.net.EthernetManager; import android.net.EthernetManager.TetheredInterfaceRequest; +import android.net.IIntResultListener; import android.net.INetd; import android.net.ITetheringEventCallback; import android.net.InetAddresses; @@ -499,10 +500,16 @@ public class TetheringTest { return new Tethering(mTetheringDependencies); } - private TetheringRequestParcel createTetheringRquestParcel(final int type) { + private TetheringRequestParcel createTetheringRequestParcel(final int type) { + return createTetheringRequestParcel(type, null, null); + } + + private TetheringRequestParcel createTetheringRequestParcel(final int type, + final LinkAddress serverAddr, final LinkAddress clientAddr) { final TetheringRequestParcel request = new TetheringRequestParcel(); request.tetheringType = type; - request.localIPv4Address = null; + request.localIPv4Address = serverAddr; + request.staticClientAddress = clientAddr; request.exemptFromEntitlementCheck = false; request.showProvisioningUi = false; @@ -616,7 +623,7 @@ public class TetheringTest { private void prepareNcmTethering() { // Emulate startTethering(TETHERING_NCM) called - mTethering.startTethering(createTetheringRquestParcel(TETHERING_NCM), null); + mTethering.startTethering(createTetheringRequestParcel(TETHERING_NCM), null); mLooper.dispatchAll(); verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NCM); @@ -629,7 +636,7 @@ public class TetheringTest { .thenReturn(upstreamState); // Emulate pressing the USB tethering button in Settings UI. - mTethering.startTethering(createTetheringRquestParcel(TETHERING_USB), null); + mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB), null); mLooper.dispatchAll(); verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS); @@ -903,7 +910,7 @@ public class TetheringTest { when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true); // Emulate pressing the WiFi tethering button. - mTethering.startTethering(createTetheringRquestParcel(TETHERING_WIFI), null); + mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null); mLooper.dispatchAll(); verify(mWifiManager, times(1)).startTetheredHotspot(null); verifyNoMoreInteractions(mWifiManager); @@ -931,7 +938,7 @@ public class TetheringTest { when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true); // Emulate pressing the WiFi tethering button. - mTethering.startTethering(createTetheringRquestParcel(TETHERING_WIFI), null); + mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null); mLooper.dispatchAll(); verify(mWifiManager, times(1)).startTetheredHotspot(null); verifyNoMoreInteractions(mWifiManager); @@ -1008,7 +1015,7 @@ public class TetheringTest { doThrow(new RemoteException()).when(mNetd).ipfwdEnableForwarding(TETHERING_NAME); // Emulate pressing the WiFi tethering button. - mTethering.startTethering(createTetheringRquestParcel(TETHERING_WIFI), null); + mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null); mLooper.dispatchAll(); verify(mWifiManager, times(1)).startTetheredHotspot(null); verifyNoMoreInteractions(mWifiManager); @@ -1303,7 +1310,7 @@ public class TetheringTest { tetherState = callback.pollTetherStatesChanged(); assertArrayEquals(tetherState.availableList, new String[] {TEST_WLAN_IFNAME}); - mTethering.startTethering(createTetheringRquestParcel(TETHERING_WIFI), null); + mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null); sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED); mLooper.dispatchAll(); tetherState = callback.pollTetherStatesChanged(); @@ -1398,10 +1405,10 @@ public class TetheringTest { public void testNoDuplicatedEthernetRequest() throws Exception { final TetheredInterfaceRequest mockRequest = mock(TetheredInterfaceRequest.class); when(mEm.requestTetheredInterface(any(), any())).thenReturn(mockRequest); - mTethering.startTethering(createTetheringRquestParcel(TETHERING_ETHERNET), null); + mTethering.startTethering(createTetheringRequestParcel(TETHERING_ETHERNET), null); mLooper.dispatchAll(); verify(mEm, times(1)).requestTetheredInterface(any(), any()); - mTethering.startTethering(createTetheringRquestParcel(TETHERING_ETHERNET), null); + mTethering.startTethering(createTetheringRequestParcel(TETHERING_ETHERNET), null); mLooper.dispatchAll(); verifyNoMoreInteractions(mEm); mTethering.stopTethering(TETHERING_ETHERNET); @@ -1580,6 +1587,86 @@ public class TetheringTest { assertTrue(element + " not found in " + collection, collection.contains(element)); } + private class ResultListener extends IIntResultListener.Stub { + private final int mExpectedResult; + private boolean mHasResult = false; + ResultListener(final int expectedResult) { + mExpectedResult = expectedResult; + } + + @Override + public void onResult(final int resultCode) { + mHasResult = true; + if (resultCode != mExpectedResult) { + fail("expected result: " + mExpectedResult + " but actual result: " + resultCode); + } + } + + public void assertHasResult() { + if (!mHasResult) fail("No callback result"); + } + } + + @Test + public void testMultipleStartTethering() throws Exception { + final LinkAddress serverLinkAddr = new LinkAddress("192.168.20.1/24"); + final LinkAddress clientLinkAddr = new LinkAddress("192.168.20.42/24"); + final String serverAddr = "192.168.20.1"; + final ResultListener firstResult = new ResultListener(TETHER_ERROR_NO_ERROR); + final ResultListener secondResult = new ResultListener(TETHER_ERROR_NO_ERROR); + final ResultListener thirdResult = new ResultListener(TETHER_ERROR_NO_ERROR); + + // Enable USB tethering and check that Tethering starts USB. + mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB, + null, null), firstResult); + mLooper.dispatchAll(); + firstResult.assertHasResult(); + verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS); + verifyNoMoreInteractions(mUsbManager); + + // Enable USB tethering again with the same request and expect no change to USB. + mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB, + null, null), secondResult); + mLooper.dispatchAll(); + secondResult.assertHasResult(); + verify(mUsbManager, never()).setCurrentFunctions(UsbManager.FUNCTION_NONE); + reset(mUsbManager); + + // Enable USB tethering with a different request and expect that USB is stopped and + // started. + mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB, + serverLinkAddr, clientLinkAddr), thirdResult); + mLooper.dispatchAll(); + thirdResult.assertHasResult(); + verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NONE); + verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS); + + // Expect that when USB comes up, the DHCP server is configured with the requested address. + mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true); + sendUsbBroadcast(true, true, true, TETHERING_USB); + mLooper.dispatchAll(); + verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks( + any(), any()); + verify(mNetd).interfaceSetCfg(argThat(cfg -> serverAddr.equals(cfg.ipv4Addr))); + } + + @Test + public void testRequestStaticServerIp() throws Exception { + final LinkAddress serverLinkAddr = new LinkAddress("192.168.20.1/24"); + final LinkAddress clientLinkAddr = new LinkAddress("192.168.20.42/24"); + final String serverAddr = "192.168.20.1"; + mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB, + serverLinkAddr, clientLinkAddr), null); + mLooper.dispatchAll(); + verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS); + mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true); + sendUsbBroadcast(true, true, true, TETHERING_USB); + mLooper.dispatchAll(); + verify(mNetd).interfaceSetCfg(argThat(cfg -> serverAddr.equals(cfg.ipv4Addr))); + + // TODO: test static client address. + } + // TODO: Test that a request for hotspot mode doesn't interfere with an // already operating tethering mode interface. } From dc651f03da7e6839fb2dd64542772649e58e44d1 Mon Sep 17 00:00:00 2001 From: markchien Date: Wed, 18 Mar 2020 08:43:07 +0800 Subject: [PATCH 056/188] Expose netId by adding getter API Bug: 151156820 Test: m atest TetetheringTests Change-Id: Ieb1483c146aa2f7d8f251157e6e81d71c44ae899 (cherry picked from commit 8831c4b54178753013d568c0b76bbf7487db8122) --- .../com/android/server/connectivity/tethering/Tethering.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java index 3d8dbab7d4..3d25e5453d 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java +++ b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java @@ -1458,7 +1458,7 @@ public class Tethering { } else { dnsServers = mConfig.defaultIPv4DNS; } - final int netId = (network != null) ? network.netId : NETID_UNSET; + final int netId = (network != null) ? network.getNetId() : NETID_UNSET; try { mNetd.tetherDnsSet(netId, dnsServers); mLog.log(String.format( From f1332573bbbee150cf701beeedf20ea220a930ab Mon Sep 17 00:00:00 2001 From: markchien Date: Thu, 19 Mar 2020 13:37:43 +0800 Subject: [PATCH 057/188] TetheringManager API clean up Per API review: - @IntDef defined on the type integer parameter - have getters on each parameter that is set in the TetheringRequest.Builder - new added API should not be deprecated Below APIs is moved from system-current to module-lib-current that only plafrom code(e.g. ConnectivityManager and Settings) can use them. TetheringRequest. onTetherableInterfaceRegexpsChanged, TetheringInterfaceRegexps: Only platform code can use them because interfaces by regular expressions are a mechanism which is planning to be deprecated. Also rename some constants for easier to understand. Bug: 149858697 Bug: 151243337 Test: m doc-comment-check-docs atest TetheringTests Change-Id: Idd041f0fbeca411ea23e49786a50dd7feb77ef45 --- .../TetheringLib/api/module-lib-current.txt | 49 +++--- .../TetheringLib/api/system-current.txt | 46 +++-- .../src/android/net/TetheringManager.java | 158 +++++++++++++----- Tethering/src/android/net/ip/IpServer.java | 4 +- .../tethering/EntitlementManager.java | 10 +- .../connectivity/tethering/Tethering.java | 11 +- .../unit/src/android/net/ip/IpServerTest.java | 4 +- .../tethering/EntitlementManagerTest.java | 27 +-- 8 files changed, 190 insertions(+), 119 deletions(-) diff --git a/Tethering/common/TetheringLib/api/module-lib-current.txt b/Tethering/common/TetheringLib/api/module-lib-current.txt index 8a7e975051..e823e17892 100644 --- a/Tethering/common/TetheringLib/api/module-lib-current.txt +++ b/Tethering/common/TetheringLib/api/module-lib-current.txt @@ -62,19 +62,20 @@ package android.net { field public static final int TETHERING_WIFI = 0; // 0x0 field public static final int TETHERING_WIFI_P2P = 3; // 0x3 field public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; // 0xc - field public static final int TETHER_ERROR_DISABLE_NAT_ERROR = 9; // 0x9 - field public static final int TETHER_ERROR_ENABLE_NAT_ERROR = 8; // 0x8 + field public static final int TETHER_ERROR_DISABLE_FORWARDING_ERROR = 9; // 0x9 + field public static final int TETHER_ERROR_ENABLE_FORWARDING_ERROR = 8; // 0x8 field public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; // 0xd field public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; // 0xa - field public static final int TETHER_ERROR_MASTER_ERROR = 5; // 0x5 + field public static final int TETHER_ERROR_INTERNAL_ERROR = 5; // 0x5 field public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; // 0xf field public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; // 0xe field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0 - field public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb + field public static final int TETHER_ERROR_PROVISIONING_FAILED = 11; // 0xb field public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; // 0x2 field public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; // 0x6 field public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; // 0x4 field public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; // 0x1 + field public static final int TETHER_ERROR_UNKNOWN_TYPE = 16; // 0x10 field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3 field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7 field public static final int TETHER_HARDWARE_OFFLOAD_FAILED = 2; // 0x2 @@ -86,29 +87,26 @@ package android.net { method public void onTetheringEntitlementResult(int); } - public abstract static class TetheringManager.StartTetheringCallback { - ctor public TetheringManager.StartTetheringCallback(); - method public void onTetheringFailed(int); - method public void onTetheringStarted(); + public static interface TetheringManager.StartTetheringCallback { + method public default void onTetheringFailed(int); + method public default void onTetheringStarted(); } - public abstract static class TetheringManager.TetheringEventCallback { - ctor public TetheringManager.TetheringEventCallback(); - method public void onClientsChanged(@NonNull java.util.Collection); - method public void onError(@NonNull String, int); - method public void onOffloadStatusChanged(int); - method @Deprecated public void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps); - method public void onTetherableInterfacesChanged(@NonNull java.util.List); - method public void onTetheredInterfacesChanged(@NonNull java.util.List); - method public void onTetheringSupported(boolean); - method public void onUpstreamChanged(@Nullable android.net.Network); + 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 onOffloadStatusChanged(int); + method public default void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps); + method public default void onTetherableInterfacesChanged(@NonNull java.util.List); + method public default void onTetheredInterfacesChanged(@NonNull java.util.List); + method public default void onTetheringSupported(boolean); + method public default void onUpstreamChanged(@Nullable android.net.Network); } - @Deprecated public static class TetheringManager.TetheringInterfaceRegexps { - ctor @Deprecated public TetheringManager.TetheringInterfaceRegexps(@NonNull String[], @NonNull String[], @NonNull String[]); - method @Deprecated @NonNull public java.util.List getTetherableBluetoothRegexs(); - method @Deprecated @NonNull public java.util.List getTetherableUsbRegexs(); - method @Deprecated @NonNull public java.util.List getTetherableWifiRegexs(); + 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(); } public static class TetheringManager.TetheringRequest { @@ -119,8 +117,11 @@ package android.net { method @NonNull public android.net.TetheringManager.TetheringRequest build(); method @Nullable public android.net.LinkAddress getClientStaticIpv4Address(); method @Nullable public android.net.LinkAddress getLocalIpv4Address(); + method public boolean getShouldShowEntitlementUi(); + method public int getTetheringType(); + method public boolean isExemptFromEntitlementCheck(); method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean); - method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setSilentProvisioning(boolean); + method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setShouldShowEntitlementUi(boolean); method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setStaticIpv4Addresses(@NonNull android.net.LinkAddress, @NonNull android.net.LinkAddress); } diff --git a/Tethering/common/TetheringLib/api/system-current.txt b/Tethering/common/TetheringLib/api/system-current.txt index ac739532a4..375113553d 100644 --- a/Tethering/common/TetheringLib/api/system-current.txt +++ b/Tethering/common/TetheringLib/api/system-current.txt @@ -40,19 +40,20 @@ package android.net { field public static final int TETHERING_WIFI = 0; // 0x0 field public static final int TETHERING_WIFI_P2P = 3; // 0x3 field public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; // 0xc - field public static final int TETHER_ERROR_DISABLE_NAT_ERROR = 9; // 0x9 - field public static final int TETHER_ERROR_ENABLE_NAT_ERROR = 8; // 0x8 + field public static final int TETHER_ERROR_DISABLE_FORWARDING_ERROR = 9; // 0x9 + field public static final int TETHER_ERROR_ENABLE_FORWARDING_ERROR = 8; // 0x8 field public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; // 0xd field public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; // 0xa - field public static final int TETHER_ERROR_MASTER_ERROR = 5; // 0x5 + field public static final int TETHER_ERROR_INTERNAL_ERROR = 5; // 0x5 field public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; // 0xf field public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; // 0xe field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0 - field public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb + field public static final int TETHER_ERROR_PROVISIONING_FAILED = 11; // 0xb field public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; // 0x2 field public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; // 0x6 field public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; // 0x4 field public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; // 0x1 + field public static final int TETHER_ERROR_UNKNOWN_TYPE = 16; // 0x10 field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3 field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7 field public static final int TETHER_HARDWARE_OFFLOAD_FAILED = 2; // 0x2 @@ -64,29 +65,19 @@ package android.net { method public void onTetheringEntitlementResult(int); } - public abstract static class TetheringManager.StartTetheringCallback { - ctor public TetheringManager.StartTetheringCallback(); - method public void onTetheringFailed(int); - method public void onTetheringStarted(); + public static interface TetheringManager.StartTetheringCallback { + method public default void onTetheringFailed(int); + method public default void onTetheringStarted(); } - public abstract static class TetheringManager.TetheringEventCallback { - ctor public TetheringManager.TetheringEventCallback(); - method public void onClientsChanged(@NonNull java.util.Collection); - method public void onError(@NonNull String, int); - method public void onOffloadStatusChanged(int); - method @Deprecated public void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps); - method public void onTetherableInterfacesChanged(@NonNull java.util.List); - method public void onTetheredInterfacesChanged(@NonNull java.util.List); - method public void onTetheringSupported(boolean); - method public void onUpstreamChanged(@Nullable android.net.Network); - } - - @Deprecated public static class TetheringManager.TetheringInterfaceRegexps { - ctor @Deprecated public TetheringManager.TetheringInterfaceRegexps(@NonNull String[], @NonNull String[], @NonNull String[]); - method @Deprecated @NonNull public java.util.List getTetherableBluetoothRegexs(); - method @Deprecated @NonNull public java.util.List getTetherableUsbRegexs(); - method @Deprecated @NonNull public java.util.List getTetherableWifiRegexs(); + 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 onOffloadStatusChanged(int); + method public default void onTetherableInterfacesChanged(@NonNull java.util.List); + method public default void onTetheredInterfacesChanged(@NonNull java.util.List); + method public default void onTetheringSupported(boolean); + method public default void onUpstreamChanged(@Nullable android.net.Network); } public static class TetheringManager.TetheringRequest { @@ -97,8 +88,11 @@ package android.net { method @NonNull public android.net.TetheringManager.TetheringRequest build(); method @Nullable public android.net.LinkAddress getClientStaticIpv4Address(); method @Nullable public android.net.LinkAddress getLocalIpv4Address(); + method public boolean getShouldShowEntitlementUi(); + method public int getTetheringType(); + method public boolean isExemptFromEntitlementCheck(); method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean); - method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setSilentProvisioning(boolean); + method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setShouldShowEntitlementUi(boolean); method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setStaticIpv4Addresses(@NonNull android.net.LinkAddress, @NonNull android.net.LinkAddress); } diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java index f2045df947..86ef7f06dc 100644 --- a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java +++ b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java @@ -115,6 +115,19 @@ public class TetheringManager { */ public static final String EXTRA_ERRORED_TETHER = "erroredArray"; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = false, value = { + TETHERING_WIFI, + TETHERING_USB, + TETHERING_BLUETOOTH, + TETHERING_WIFI_P2P, + TETHERING_NCM, + TETHERING_ETHERNET, + }) + public @interface TetheringType { + } + /** * Invalid tethering type. * @see #startTethering. @@ -158,22 +171,60 @@ public class TetheringManager { */ public static final int TETHERING_ETHERNET = 5; - public static final int TETHER_ERROR_NO_ERROR = 0; - public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; - public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; - public static final int TETHER_ERROR_UNSUPPORTED = 3; - public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; - public static final int TETHER_ERROR_MASTER_ERROR = 5; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = { + TETHER_ERROR_NO_ERROR, + TETHER_ERROR_PROVISIONING_FAILED, + TETHER_ERROR_ENTITLEMENT_UNKNOWN, + }) + public @interface EntitlementResult { + } + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = { + TETHER_ERROR_NO_ERROR, + TETHER_ERROR_UNKNOWN_IFACE, + TETHER_ERROR_SERVICE_UNAVAIL, + TETHER_ERROR_INTERNAL_ERROR, + TETHER_ERROR_TETHER_IFACE_ERROR, + TETHER_ERROR_ENABLE_FORWARDING_ERROR, + TETHER_ERROR_DISABLE_FORWARDING_ERROR, + TETHER_ERROR_IFACE_CFG_ERROR, + TETHER_ERROR_DHCPSERVER_ERROR, + }) + public @interface TetheringIfaceError { + } + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = { + TETHER_ERROR_SERVICE_UNAVAIL, + TETHER_ERROR_INTERNAL_ERROR, + TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION, + TETHER_ERROR_UNKNOWN_TYPE, + }) + public @interface StartTetheringError { + } + + public static final int TETHER_ERROR_NO_ERROR = 0; + public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; + public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; + public static final int TETHER_ERROR_UNSUPPORTED = 3; + public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; + public static final int TETHER_ERROR_INTERNAL_ERROR = 5; public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; - public static final int TETHER_ERROR_ENABLE_NAT_ERROR = 8; - public static final int TETHER_ERROR_DISABLE_NAT_ERROR = 9; - public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; - public static final int TETHER_ERROR_PROVISION_FAILED = 11; - public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; + public static final int TETHER_ERROR_ENABLE_FORWARDING_ERROR = 8; + public static final int TETHER_ERROR_DISABLE_FORWARDING_ERROR = 9; + public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; + public static final int TETHER_ERROR_PROVISIONING_FAILED = 11; + public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; + public static final int TETHER_ERROR_UNKNOWN_TYPE = 16; /** @hide */ @Retention(RetentionPolicy.SOURCE) @@ -508,7 +559,7 @@ public class TetheringManager { private final TetheringRequestParcel mBuilderParcel; /** Default constructor of Builder. */ - public Builder(final int type) { + public Builder(@TetheringType final int type) { mBuilderParcel = new TetheringRequestParcel(); mBuilderParcel.tetheringType = type; mBuilderParcel.localIPv4Address = null; @@ -553,11 +604,14 @@ public class TetheringManager { return this; } - /** Start tethering without showing the provisioning UI. */ + /** + * If an entitlement check is needed, sets whether to show the entitlement UI or to + * perform a silent entitlement check. By default, the entitlement UI is shown. + */ @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) @NonNull - public Builder setSilentProvisioning(boolean silent) { - mBuilderParcel.showProvisioningUi = silent; + public Builder setShouldShowEntitlementUi(boolean showUi) { + mBuilderParcel.showProvisioningUi = showUi; return this; } @@ -567,7 +621,6 @@ public class TetheringManager { return new TetheringRequest(mBuilderParcel); } - /** Get static server address. */ @Nullable public LinkAddress getLocalIpv4Address() { return mBuilderParcel.localIPv4Address; @@ -578,6 +631,22 @@ public class TetheringManager { public LinkAddress getClientStaticIpv4Address() { return mBuilderParcel.staticClientAddress; } + + /** Get tethering type. */ + @TetheringType + public int getTetheringType() { + return mBuilderParcel.tetheringType; + } + + /** Check if exempt from entitlement check. */ + public boolean isExemptFromEntitlementCheck() { + return mBuilderParcel.exemptFromEntitlementCheck; + } + + /** Check if show entitlement ui. */ + public boolean getShouldShowEntitlementUi() { + return mBuilderParcel.showProvisioningUi; + } } /** @@ -602,18 +671,18 @@ public class TetheringManager { /** * Callback for use with {@link #startTethering} to find out whether tethering succeeded. */ - public abstract static class StartTetheringCallback { + public interface StartTetheringCallback { /** * Called when tethering has been successfully started. */ - public void onTetheringStarted() {} + default void onTetheringStarted() {} /** * Called when starting tethering failed. * - * @param resultCode One of the {@code TETHER_ERROR_*} constants. + * @param error The error that caused the failure. */ - public void onTetheringFailed(final int resultCode) {} + default void onTetheringFailed(@StartTetheringError final int error) {} } /** @@ -684,7 +753,7 @@ public class TetheringManager { android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS }) - public void stopTethering(final int type) { + public void stopTethering(@TetheringType final int type) { final String callerPkg = mContext.getOpPackageName(); Log.i(TAG, "stopTethering caller:" + callerPkg); @@ -709,10 +778,10 @@ public class TetheringManager { * * @param resultCode an int value of entitlement result. It may be one of * {@link #TETHER_ERROR_NO_ERROR}, - * {@link #TETHER_ERROR_PROVISION_FAILED}, or + * {@link #TETHER_ERROR_PROVISIONING_FAILED}, or * {@link #TETHER_ERROR_ENTITLEMENT_UNKNOWN}. */ - void onTetheringEntitlementResult(int resultCode); + void onTetheringEntitlementResult(@EntitlementResult int result); } /** @@ -727,7 +796,8 @@ public class TetheringManager { * fail if a tethering entitlement check is required. * * @param type the downstream type of tethering. Must be one of {@code #TETHERING_*} constants. - * @param showEntitlementUi a boolean indicating whether to run UI-based entitlement check. + * @param showEntitlementUi a boolean indicating whether to check result for the UI-based + * entitlement check or the silent entitlement check. * @param executor the executor on which callback will be invoked. * @param listener an {@link OnTetheringEntitlementResultListener} which will be called to * notify the caller of the result of entitlement check. The listener may be called zero @@ -737,7 +807,8 @@ public class TetheringManager { android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS }) - public void requestLatestTetheringEntitlementResult(int type, boolean showEntitlementUi, + public void requestLatestTetheringEntitlementResult(@TetheringType int type, + boolean showEntitlementUi, @NonNull Executor executor, @NonNull final OnTetheringEntitlementResultListener listener) { if (listener == null) { @@ -766,7 +837,7 @@ public class TetheringManager { */ // TODO: improve the usage of ResultReceiver, b/145096122 @SystemApi(client = MODULE_LIBRARIES) - public void requestLatestTetheringEntitlementResult(final int type, + public void requestLatestTetheringEntitlementResult(@TetheringType final int type, @NonNull final ResultReceiver receiver, final boolean showEntitlementUi) { final String callerPkg = mContext.getOpPackageName(); Log.i(TAG, "getLatestTetheringEntitlementResult caller:" + callerPkg); @@ -779,7 +850,7 @@ public class TetheringManager { * Callback for use with {@link registerTetheringEventCallback} to find out tethering * upstream status. */ - public abstract static class TetheringEventCallback { + public interface TetheringEventCallback { /** * Called when tethering supported status changed. * @@ -791,7 +862,7 @@ public class TetheringManager { * * @param supported The new supported status */ - public void onTetheringSupported(boolean supported) {} + default void onTetheringSupported(boolean supported) {} /** * Called when tethering upstream changed. @@ -802,7 +873,7 @@ public class TetheringManager { * @param network the {@link Network} of tethering upstream. Null means tethering doesn't * have any upstream. */ - public void onUpstreamChanged(@Nullable Network network) {} + default void onUpstreamChanged(@Nullable Network network) {} /** * Called when there was a change in tethering interface regular expressions. @@ -810,28 +881,30 @@ public class TetheringManager { *

This will be called immediately after the callback is registered, and may be called * multiple times later upon changes. * @param reg The new regular expressions. - * @deprecated Referencing interfaces by regular expressions is a deprecated mechanism. + * + * @hide */ - @Deprecated - public void onTetherableInterfaceRegexpsChanged(@NonNull TetheringInterfaceRegexps reg) {} + @SystemApi(client = MODULE_LIBRARIES) + default void onTetherableInterfaceRegexpsChanged(@NonNull TetheringInterfaceRegexps reg) {} /** - * Called when there was a change in the list of tetherable 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 list of tetherable interfaces. + * @param interfaces The list of tetherable interface names. */ - public void onTetherableInterfacesChanged(@NonNull List interfaces) {} + default void onTetherableInterfacesChanged(@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 list of tethered interfaces. + * @param interfaces The list of 0 or more String of currently tethered interface names. */ - public void onTetheredInterfacesChanged(@NonNull List interfaces) {} + default void onTetheredInterfacesChanged(@NonNull List interfaces) {} /** * Called when an error occurred configuring tethering. @@ -841,7 +914,7 @@ public class TetheringManager { * @param ifName Name of the interface. * @param error One of {@code TetheringManager#TETHER_ERROR_*}. */ - public void onError(@NonNull String ifName, int error) {} + default void onError(@NonNull String ifName, @TetheringIfaceError int error) {} /** * Called when the list of tethered clients changes. @@ -854,7 +927,7 @@ public class TetheringManager { * determine if they are still connected. * @param clients The new set of tethered clients; the collection is not ordered. */ - public void onClientsChanged(@NonNull Collection clients) {} + default void onClientsChanged(@NonNull Collection clients) {} /** * Called when tethering offload status changes. @@ -862,19 +935,20 @@ public class TetheringManager { *

This will be called immediately after the callback is registered. * @param status The offload status. */ - public void onOffloadStatusChanged(@TetherOffloadStatus int status) {} + default void onOffloadStatusChanged(@TetherOffloadStatus int status) {} } /** * Regular expressions used to identify tethering interfaces. - * @deprecated Referencing interfaces by regular expressions is a deprecated mechanism. + * @hide */ - @Deprecated + @SystemApi(client = MODULE_LIBRARIES) public static class TetheringInterfaceRegexps { private final String[] mTetherableBluetoothRegexs; private final String[] mTetherableUsbRegexs; private final String[] mTetherableWifiRegexs; + /** @hide */ public TetheringInterfaceRegexps(@NonNull String[] tetherableBluetoothRegexs, @NonNull String[] tetherableUsbRegexs, @NonNull String[] tetherableWifiRegexs) { mTetherableBluetoothRegexs = tetherableBluetoothRegexs.clone(); diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java index 433b903ebe..c5478d2e1a 100644 --- a/Tethering/src/android/net/ip/IpServer.java +++ b/Tethering/src/android/net/ip/IpServer.java @@ -1054,7 +1054,7 @@ public class IpServer extends StateMachine { case CMD_START_TETHERING_ERROR: case CMD_STOP_TETHERING_ERROR: case CMD_SET_DNS_FORWARDERS_ERROR: - mLastError = TetheringManager.TETHER_ERROR_MASTER_ERROR; + mLastError = TetheringManager.TETHER_ERROR_INTERNAL_ERROR; transitionTo(mInitialState); break; default: @@ -1185,7 +1185,7 @@ public class IpServer extends StateMachine { } catch (RemoteException | ServiceSpecificException e) { mLog.e("Exception enabling NAT: " + e.toString()); cleanupUpstream(); - mLastError = TetheringManager.TETHER_ERROR_ENABLE_NAT_ERROR; + mLastError = TetheringManager.TETHER_ERROR_ENABLE_FORWARDING_ERROR; transitionTo(mInitialState); return true; } diff --git a/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java b/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java index e81d6ac7a5..bd60594f27 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java +++ b/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java @@ -25,7 +25,7 @@ import static android.net.TetheringManager.TETHERING_USB; import static android.net.TetheringManager.TETHERING_WIFI; import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN; import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; -import static android.net.TetheringManager.TETHER_ERROR_PROVISION_FAILED; +import static android.net.TetheringManager.TETHER_ERROR_PROVISIONING_FAILED; import android.app.AlarmManager; import android.app.PendingIntent; @@ -579,7 +579,7 @@ public class EntitlementManager { switch (value) { case TETHER_ERROR_ENTITLEMENT_UNKNOWN: return "TETHER_ERROR_ENTITLEMENT_UNKONWN"; case TETHER_ERROR_NO_ERROR: return "TETHER_ERROR_NO_ERROR"; - case TETHER_ERROR_PROVISION_FAILED: return "TETHER_ERROR_PROVISION_FAILED"; + case TETHER_ERROR_PROVISIONING_FAILED: return "TETHER_ERROR_PROVISIONING_FAILED"; default: return String.format("UNKNOWN ERROR (%d)", value); } @@ -592,7 +592,7 @@ public class EntitlementManager { protected void onReceiveResult(int resultCode, Bundle resultData) { int updatedCacheValue = updateEntitlementCacheValue(type, resultCode); addDownstreamMapping(type, updatedCacheValue); - if (updatedCacheValue == TETHER_ERROR_PROVISION_FAILED && notifyFail) { + if (updatedCacheValue == TETHER_ERROR_PROVISIONING_FAILED && notifyFail) { mListener.onUiEntitlementFailed(type); } if (receiver != null) receiver.send(updatedCacheValue, null); @@ -635,8 +635,8 @@ public class EntitlementManager { mEntitlementCacheValue.put(type, resultCode); return resultCode; } else { - mEntitlementCacheValue.put(type, TETHER_ERROR_PROVISION_FAILED); - return TETHER_ERROR_PROVISION_FAILED; + mEntitlementCacheValue.put(type, TETHER_ERROR_PROVISIONING_FAILED); + return TETHER_ERROR_PROVISIONING_FAILED; } } diff --git a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java index 6e918929f8..8e2d4f4308 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java +++ b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java @@ -39,11 +39,12 @@ import static android.net.TetheringManager.TETHERING_NCM; import static android.net.TetheringManager.TETHERING_USB; import static android.net.TetheringManager.TETHERING_WIFI; import static android.net.TetheringManager.TETHERING_WIFI_P2P; -import static android.net.TetheringManager.TETHER_ERROR_MASTER_ERROR; +import static android.net.TetheringManager.TETHER_ERROR_INTERNAL_ERROR; import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; import static android.net.TetheringManager.TETHER_ERROR_SERVICE_UNAVAIL; import static android.net.TetheringManager.TETHER_ERROR_UNAVAIL_IFACE; import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_IFACE; +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; @@ -545,7 +546,7 @@ public class Tethering { break; default: Log.w(TAG, "Invalid tether type."); - result = TETHER_ERROR_UNKNOWN_IFACE; + result = TETHER_ERROR_UNKNOWN_TYPE; } // The result of Bluetooth tethering will be sent by #setBluetoothTethering. @@ -586,7 +587,7 @@ public class Tethering { Binder.restoreCallingIdentity(ident); } - return TETHER_ERROR_MASTER_ERROR; + return TETHER_ERROR_INTERNAL_ERROR; } private void setBluetoothTethering(final boolean enable, final IIntResultListener listener) { @@ -622,7 +623,7 @@ public class Tethering { // We should figure out a way to bubble up that failure instead of sending success. final int result = (((BluetoothPan) proxy).isTetheringOn() == enable) ? TETHER_ERROR_NO_ERROR - : TETHER_ERROR_MASTER_ERROR; + : TETHER_ERROR_INTERNAL_ERROR; sendTetherResult(listener, result, TETHERING_BLUETOOTH); adapter.closeProfileProxy(BluetoothProfile.PAN, proxy); } @@ -2180,7 +2181,7 @@ public class Tethering { // If TetherMasterSM is in ErrorState, TetherMasterSM stays there. // Thus we give a chance for TetherMasterSM to recover to InitialState // by sending CMD_CLEAR_ERROR - if (error == TETHER_ERROR_MASTER_ERROR) { + if (error == TETHER_ERROR_INTERNAL_ERROR) { mTetherMasterSM.sendMessage(TetherMasterSM.CMD_CLEAR_ERROR, who); } int which; diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java index 948266d3cf..3106e0e5e1 100644 --- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java +++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java @@ -21,7 +21,7 @@ import static android.net.TetheringManager.TETHERING_BLUETOOTH; import static android.net.TetheringManager.TETHERING_USB; import static android.net.TetheringManager.TETHERING_WIFI; import static android.net.TetheringManager.TETHERING_WIFI_P2P; -import static android.net.TetheringManager.TETHER_ERROR_ENABLE_NAT_ERROR; +import static android.net.TetheringManager.TETHER_ERROR_ENABLE_FORWARDING_ERROR; import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; import static android.net.TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR; import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS; @@ -448,7 +448,7 @@ public class IpServerTest { usbTeardownOrder.verify(mNetd, times(2)).interfaceSetCfg( argThat(cfg -> IFACE_NAME.equals(cfg.ifName))); usbTeardownOrder.verify(mCallback).updateInterfaceState( - mIpServer, STATE_AVAILABLE, TETHER_ERROR_ENABLE_NAT_ERROR); + mIpServer, STATE_AVAILABLE, TETHER_ERROR_ENABLE_FORWARDING_ERROR); usbTeardownOrder.verify(mCallback).updateLinkProperties( eq(mIpServer), mLinkPropertiesCaptor.capture()); assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue()); diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java index 3a1d4a61a3..0a7850b680 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java +++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java @@ -21,7 +21,7 @@ import static android.net.TetheringManager.TETHERING_USB; import static android.net.TetheringManager.TETHERING_WIFI; import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN; import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; -import static android.net.TetheringManager.TETHER_ERROR_PROVISION_FAILED; +import static android.net.TetheringManager.TETHER_ERROR_PROVISIONING_FAILED; import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; @@ -284,11 +284,11 @@ public final class EntitlementManagerTest { assertEquals(0, mEnMgr.uiProvisionCount); mEnMgr.reset(); // 3. No cache value and ui entitlement check is needed. - mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED; + mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED; receiver = new ResultReceiver(null) { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { - assertEquals(TETHER_ERROR_PROVISION_FAILED, resultCode); + assertEquals(TETHER_ERROR_PROVISIONING_FAILED, resultCode); mCallbacklatch.countDown(); } }; @@ -297,12 +297,13 @@ public final class EntitlementManagerTest { callbackTimeoutHelper(mCallbacklatch); assertEquals(1, mEnMgr.uiProvisionCount); mEnMgr.reset(); - // 4. Cache value is TETHER_ERROR_PROVISION_FAILED and don't need to run entitlement check. + // 4. Cache value is TETHER_ERROR_PROVISIONING_FAILED and don't need to run entitlement + // check. mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; receiver = new ResultReceiver(null) { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { - assertEquals(TETHER_ERROR_PROVISION_FAILED, resultCode); + assertEquals(TETHER_ERROR_PROVISIONING_FAILED, resultCode); mCallbacklatch.countDown(); } }; @@ -311,7 +312,7 @@ public final class EntitlementManagerTest { callbackTimeoutHelper(mCallbacklatch); assertEquals(0, mEnMgr.uiProvisionCount); mEnMgr.reset(); - // 5. Cache value is TETHER_ERROR_PROVISION_FAILED and ui entitlement check is needed. + // 5. Cache value is TETHER_ERROR_PROVISIONING_FAILED and ui entitlement check is needed. mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; receiver = new ResultReceiver(null) { @Override @@ -364,7 +365,7 @@ public final class EntitlementManagerTest { public void verifyPermissionResult() { setupForRequiredProvisioning(); mEnMgr.notifyUpstream(true); - mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED; + mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED; mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true); mLooper.dispatchAll(); assertFalse(mEnMgr.isCellularUpstreamPermitted()); @@ -380,15 +381,15 @@ public final class EntitlementManagerTest { public void verifyPermissionIfAllNotApproved() { setupForRequiredProvisioning(); mEnMgr.notifyUpstream(true); - mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED; + mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED; mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true); mLooper.dispatchAll(); assertFalse(mEnMgr.isCellularUpstreamPermitted()); - mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED; + mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED; mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true); mLooper.dispatchAll(); assertFalse(mEnMgr.isCellularUpstreamPermitted()); - mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED; + mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED; mEnMgr.startProvisioningIfNeeded(TETHERING_BLUETOOTH, true); mLooper.dispatchAll(); assertFalse(mEnMgr.isCellularUpstreamPermitted()); @@ -403,7 +404,7 @@ public final class EntitlementManagerTest { mLooper.dispatchAll(); assertTrue(mEnMgr.isCellularUpstreamPermitted()); mLooper.dispatchAll(); - mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED; + mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED; mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true); mLooper.dispatchAll(); assertTrue(mEnMgr.isCellularUpstreamPermitted()); @@ -465,7 +466,7 @@ public final class EntitlementManagerTest { assertEquals(0, mEnMgr.silentProvisionCount); mEnMgr.reset(); // 6. switch upstream back to mobile again - mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED; + mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED; mEnMgr.notifyUpstream(true); mLooper.dispatchAll(); assertEquals(0, mEnMgr.uiProvisionCount); @@ -477,7 +478,7 @@ public final class EntitlementManagerTest { public void testCallStopTetheringWhenUiProvisioningFail() { setupForRequiredProvisioning(); verify(mEntitlementFailedListener, times(0)).onUiEntitlementFailed(TETHERING_WIFI); - mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED; + mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED; mEnMgr.notifyUpstream(true); mLooper.dispatchAll(); mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true); From ac580f982636ef522f3b435bf26f8bd7f50aa589 Mon Sep 17 00:00:00 2001 From: markchien Date: Wed, 18 Mar 2020 21:16:15 +0800 Subject: [PATCH 058/188] Move NetworkCallback to last parameter for new exposed requestNetwork Bug: 151243698 Test: atest TetheringTests Change-Id: I87ef1d451eefa6998b9793c4eacabae978376d24 --- .../connectivity/tethering/UpstreamNetworkMonitor.java | 3 ++- .../tethering/UpstreamNetworkMonitorTest.java | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java index 2875f71e5e..7ac7f5f06e 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java +++ b/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java @@ -244,7 +244,8 @@ public class UpstreamNetworkMonitor { // Additionally, we log a message to aid in any subsequent debugging. mLog.i("requesting mobile upstream network: " + mobileUpstreamRequest); - cm().requestNetwork(mobileUpstreamRequest, mMobileNetworkCallback, 0, legacyType, mHandler); + cm().requestNetwork(mobileUpstreamRequest, 0, legacyType, mHandler, + mMobileNetworkCallback); } /** Release mobile network request. */ diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java index 5ed75bf26f..7c98f626a4 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java +++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java @@ -212,8 +212,8 @@ public class UpstreamNetworkMonitorTest { mUNM.updateMobileRequiresDun(true); mUNM.registerMobileNetworkRequest(); verify(mCM, times(1)).requestNetwork( - any(NetworkRequest.class), any(NetworkCallback.class), anyInt(), anyInt(), - any(Handler.class)); + any(NetworkRequest.class), anyInt(), anyInt(), any(Handler.class), + any(NetworkCallback.class)); assertTrue(mUNM.mobileNetworkRequested()); assertUpstreamTypeRequested(TYPE_MOBILE_DUN); @@ -649,8 +649,8 @@ public class UpstreamNetworkMonitorTest { } @Override - public void requestNetwork(NetworkRequest req, NetworkCallback cb, - int timeoutMs, int legacyType, Handler h) { + public void requestNetwork(NetworkRequest req, + int timeoutMs, int legacyType, Handler h, NetworkCallback cb) { assertFalse(allCallbacks.containsKey(cb)); allCallbacks.put(cb, h); assertFalse(requested.containsKey(cb)); From 9c9aeba98f8372cfe6a39c6a73c9cb271346169f Mon Sep 17 00:00:00 2001 From: Paul Hu Date: Thu, 19 Mar 2020 11:31:58 +0000 Subject: [PATCH 059/188] [TNU1.2] Add string for client number notification Add string for client number notification Bug: 122085773 Test: atest TetheringTests Change-Id: Icc4e59ce3b2d8d4c1c7883c2f9d040d3ce563f09 Merged-In: Icc4e59ce3b2d8d4c1c7883c2f9d040d3ce563f09 (cherry picked from aosp/1259995) --- .../res/values-mcc310-mnc120/strings.xml | 22 +++++++++++++++++++ .../res/values-mcc311-mnc490/strings.xml | 22 +++++++++++++++++++ .../res/values-mcc312-mnc530/strings.xml | 22 +++++++++++++++++++ Tethering/res/values/strings.xml | 5 ++++- 4 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 Tethering/res/values-mcc310-mnc120/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc490/strings.xml create mode 100644 Tethering/res/values-mcc312-mnc530/strings.xml diff --git a/Tethering/res/values-mcc310-mnc120/strings.xml b/Tethering/res/values-mcc310-mnc120/strings.xml new file mode 100644 index 0000000000..618df90c71 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc120/strings.xml @@ -0,0 +1,22 @@ + + + + + + %1$d device connected. + %1$d devices connected. + + \ No newline at end of file diff --git a/Tethering/res/values-mcc311-mnc490/strings.xml b/Tethering/res/values-mcc311-mnc490/strings.xml new file mode 100644 index 0000000000..618df90c71 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc490/strings.xml @@ -0,0 +1,22 @@ + + + + + + %1$d device connected. + %1$d devices connected. + + \ No newline at end of file diff --git a/Tethering/res/values-mcc312-mnc530/strings.xml b/Tethering/res/values-mcc312-mnc530/strings.xml new file mode 100644 index 0000000000..618df90c71 --- /dev/null +++ b/Tethering/res/values-mcc312-mnc530/strings.xml @@ -0,0 +1,22 @@ + + + + + + %1$d device connected. + %1$d devices connected. + + \ No newline at end of file diff --git a/Tethering/res/values/strings.xml b/Tethering/res/values/strings.xml index d50884b2a7..981524e4da 100644 --- a/Tethering/res/values/strings.xml +++ b/Tethering/res/values/strings.xml @@ -13,12 +13,15 @@ See the License for the specific language governing permissions and limitations under the License. --> - + Tethering or hotspot active Tap to set up. + + + - Tethering & Mobile Hotspot has no internet access + Hotspot has no internet + + Devices can\u2019t connect to internet + + Turn off hotspot - Roaming + Hotspot is on - You will be charged on a per MB basis for all data sent or received while using this service outside the Verizon Network. Please check our website for current international rates. To minimize charges, visit My Verizon periodically to monitor your usage, check your device settings to confirm which devices are connected, and consider using alternate data connections when available + Additional charges may apply while roaming Continue - \ No newline at end of file + diff --git a/Tethering/res/values-mcc310-mnc004/strings.xml b/Tethering/res/values-mcc310-mnc004/strings.xml index 6bc2e2aa92..a996b4247a 100644 --- a/Tethering/res/values-mcc310-mnc004/strings.xml +++ b/Tethering/res/values-mcc310-mnc004/strings.xml @@ -15,12 +15,16 @@ --> - Tethering & Mobile Hotspot has no internet access + Hotspot has no internet + + Devices can\u2019t connect to internet + + Turn off hotspot - Roaming + Hotspot is on - You will be charged on a per MB basis for all data sent or received while using this service outside the Verizon Network. Please check our website for current international rates. To minimize charges, visit My Verizon periodically to monitor your usage, check your device settings to confirm which devices are connected, and consider using alternate data connections when available + Additional charges may apply while roaming Continue - \ No newline at end of file + diff --git a/Tethering/res/values-mcc311-mnc480/strings.xml b/Tethering/res/values-mcc311-mnc480/strings.xml index 6bc2e2aa92..a996b4247a 100644 --- a/Tethering/res/values-mcc311-mnc480/strings.xml +++ b/Tethering/res/values-mcc311-mnc480/strings.xml @@ -15,12 +15,16 @@ --> - Tethering & Mobile Hotspot has no internet access + Hotspot has no internet + + Devices can\u2019t connect to internet + + Turn off hotspot - Roaming + Hotspot is on - You will be charged on a per MB basis for all data sent or received while using this service outside the Verizon Network. Please check our website for current international rates. To minimize charges, visit My Verizon periodically to monitor your usage, check your device settings to confirm which devices are connected, and consider using alternate data connections when available + Additional charges may apply while roaming Continue - \ No newline at end of file + diff --git a/Tethering/res/values/strings.xml b/Tethering/res/values/strings.xml index 981524e4da..52a16545c2 100644 --- a/Tethering/res/values/strings.xml +++ b/Tethering/res/values/strings.xml @@ -38,6 +38,10 @@ + + + + @@ -45,4 +49,4 @@ - \ No newline at end of file + From aaeaa195b837c27d1f7896ca6c3eb3921c491794 Mon Sep 17 00:00:00 2001 From: Anton Hansson Date: Wed, 18 Mar 2020 14:15:42 +0000 Subject: [PATCH 064/188] Build tethering against the module SDK Link against the stubs of other modules and the implementation of the tethering module jar. Bug: 146757305 Test: m Tethering Change-Id: I7c93b60654e21a4a27d21cbf0c2a6cb21f813529 --- Tethering/Android.bp | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/Tethering/Android.bp b/Tethering/Android.bp index 0c37235129..190b443227 100644 --- a/Tethering/Android.bp +++ b/Tethering/Android.bp @@ -16,9 +16,7 @@ java_defaults { name: "TetheringAndroidLibraryDefaults", - // TODO (b/146757305): change to module API once available - // TODO (b/148190005): change to module-libs-api-stubs-current once it is ready. - sdk_version: "core_platform", + sdk_version: "module_current", srcs: [ "src/**/*.java", ":framework-tethering-shared-srcs", @@ -35,15 +33,10 @@ java_defaults { "net-utils-framework-common", ], libs: [ - // Order matters: framework-tethering needs to be before the system stubs, otherwise - // hidden fields in the framework-tethering classes (which are also used to generate stubs) - // will not be found. "framework-tethering", - "android_system_stubs_current", - "framework-res", + "framework-telephony-stubs", + "framework-wifi-stubs-systemapi", "unsupportedappusage", - "android_system_stubs_current", - "framework-res", ], plugins: ["java_api_finder"], manifest: "AndroidManifestBase.xml", @@ -91,9 +84,7 @@ cc_library { // Common defaults for compiling the actual APK. java_defaults { name: "TetheringAppDefaults", - // TODO (b/146757305): change to module API once available - // TODO (b/148190005): change to module-libs-api-stubs-current once it is ready. - sdk_version: "core_platform", + sdk_version: "module_current", privileged: true, jni_libs: [ "libtetherutilsjni", @@ -102,12 +93,7 @@ java_defaults { "res", ], libs: [ - // Order matters: framework-tethering needs to be before the system stubs, otherwise - // hidden fields in the framework-tethering classes (which are also used to generate stubs) - // will not be found. "framework-tethering", - "android_system_stubs_current", - "framework-res", ], jarjar_rules: "jarjar-rules.txt", optimize: { From e3b9708c3a60018702ec40c70648cc90ab14c7f2 Mon Sep 17 00:00:00 2001 From: junyulai Date: Mon, 23 Mar 2020 10:47:42 +0800 Subject: [PATCH 065/188] Address API council review comment about TetheringRequest Test: atest TetheringTests FrameworksNetTests NetworkStackTests Bug: 152055812 Change-Id: I0158d88e364772f9ac258bd18955edcdad266ad8 --- .../TetheringLib/api/module-lib-current.txt | 10 ++-- .../TetheringLib/api/system-current.txt | 10 ++-- .../src/android/net/TetheringManager.java | 51 +++++++++++-------- 3 files changed, 39 insertions(+), 32 deletions(-) diff --git a/Tethering/common/TetheringLib/api/module-lib-current.txt b/Tethering/common/TetheringLib/api/module-lib-current.txt index e823e17892..754584e70f 100644 --- a/Tethering/common/TetheringLib/api/module-lib-current.txt +++ b/Tethering/common/TetheringLib/api/module-lib-current.txt @@ -110,16 +110,16 @@ package android.net { } public static class TetheringManager.TetheringRequest { - } - - public static class TetheringManager.TetheringRequest.Builder { - ctor public TetheringManager.TetheringRequest.Builder(int); - method @NonNull public android.net.TetheringManager.TetheringRequest build(); method @Nullable public android.net.LinkAddress getClientStaticIpv4Address(); method @Nullable public android.net.LinkAddress getLocalIpv4Address(); method public boolean getShouldShowEntitlementUi(); method public int getTetheringType(); method public boolean isExemptFromEntitlementCheck(); + } + + public static class TetheringManager.TetheringRequest.Builder { + ctor public TetheringManager.TetheringRequest.Builder(int); + method @NonNull public android.net.TetheringManager.TetheringRequest build(); method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean); method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setShouldShowEntitlementUi(boolean); method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setStaticIpv4Addresses(@NonNull android.net.LinkAddress, @NonNull android.net.LinkAddress); diff --git a/Tethering/common/TetheringLib/api/system-current.txt b/Tethering/common/TetheringLib/api/system-current.txt index 4ac44ba41f..edd1ebb5f7 100644 --- a/Tethering/common/TetheringLib/api/system-current.txt +++ b/Tethering/common/TetheringLib/api/system-current.txt @@ -80,16 +80,16 @@ package android.net { } public static class TetheringManager.TetheringRequest { - } - - public static class TetheringManager.TetheringRequest.Builder { - ctor public TetheringManager.TetheringRequest.Builder(int); - method @NonNull public android.net.TetheringManager.TetheringRequest build(); method @Nullable public android.net.LinkAddress getClientStaticIpv4Address(); method @Nullable public android.net.LinkAddress getLocalIpv4Address(); method public boolean getShouldShowEntitlementUi(); method public int getTetheringType(); method public boolean isExemptFromEntitlementCheck(); + } + + public static class TetheringManager.TetheringRequest.Builder { + ctor public TetheringManager.TetheringRequest.Builder(int); + method @NonNull public android.net.TetheringManager.TetheringRequest build(); method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean); method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setShouldShowEntitlementUi(boolean); method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setStaticIpv4Addresses(@NonNull android.net.LinkAddress, @NonNull android.net.LinkAddress); diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java index bd65fc200c..350980137f 100644 --- a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java +++ b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java @@ -620,33 +620,40 @@ public class TetheringManager { public TetheringRequest build() { return new TetheringRequest(mBuilderParcel); } + } - @Nullable - public LinkAddress getLocalIpv4Address() { - return mBuilderParcel.localIPv4Address; - } + /** + * Get the local IPv4 address, if one was configured with + * {@link Builder#setStaticIpv4Addresses}. + */ + @Nullable + public LinkAddress getLocalIpv4Address() { + return mRequestParcel.localIPv4Address; + } - /** Get static client address. */ - @Nullable - public LinkAddress getClientStaticIpv4Address() { - return mBuilderParcel.staticClientAddress; - } + /** + * Get the static IPv4 address of the client, if one was configured with + * {@link Builder#setStaticIpv4Addresses}. + */ + @Nullable + public LinkAddress getClientStaticIpv4Address() { + return mRequestParcel.staticClientAddress; + } - /** Get tethering type. */ - @TetheringType - public int getTetheringType() { - return mBuilderParcel.tetheringType; - } + /** Get tethering type. */ + @TetheringType + public int getTetheringType() { + return mRequestParcel.tetheringType; + } - /** Check if exempt from entitlement check. */ - public boolean isExemptFromEntitlementCheck() { - return mBuilderParcel.exemptFromEntitlementCheck; - } + /** Check if exempt from entitlement check. */ + public boolean isExemptFromEntitlementCheck() { + return mRequestParcel.exemptFromEntitlementCheck; + } - /** Check if show entitlement ui. */ - public boolean getShouldShowEntitlementUi() { - return mBuilderParcel.showProvisioningUi; - } + /** Check if show entitlement ui. */ + public boolean getShouldShowEntitlementUi() { + return mRequestParcel.showProvisioningUi; } /** From 5a76a7114dad770f26deca131e3aa64a6fae7d6d Mon Sep 17 00:00:00 2001 From: markchien Date: Thu, 12 Mar 2020 15:22:01 +0800 Subject: [PATCH 066/188] Give tethering bluetooth privilege permission Permisssion of PanService#setBluetoothTethering is change from BLUETOOTH_ADMIN to BLUETOOTH_PRIVILEGED. Tethering service need bluetooth privilege permission to enable bluetooth tethering. Bug: 146045934 Test: on/off bluetooth tethering Merged-In: Ib87a5d5a5bb49390aa55e52713bb3539d4a52348 Change-Id: Ib87a5d5a5bb49390aa55e52713bb3539d4a52348 --- Tethering/AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tethering/AndroidManifest.xml b/Tethering/AndroidManifest.xml index c71d0d7bc5..9328611f5d 100644 --- a/Tethering/AndroidManifest.xml +++ b/Tethering/AndroidManifest.xml @@ -27,7 +27,7 @@ added to the privileged permissions whitelist for that package. --> - + From 70b4a2e524b5739c237c4e06eea9fc15914ff44d Mon Sep 17 00:00:00 2001 From: markchien Date: Thu, 12 Mar 2020 15:22:01 +0800 Subject: [PATCH 067/188] Give tethering bluetooth privilege permission Permisssion of PanService#setBluetoothTethering is change from BLUETOOTH_ADMIN to BLUETOOTH_PRIVILEGED. Tethering service need bluetooth privilege permission to enable bluetooth tethering. Bug: 146045934 Test: on/off bluetooth tethering Merged-In: Ib87a5d5a5bb49390aa55e52713bb3539d4a52348 Change-Id: Ib87a5d5a5bb49390aa55e52713bb3539d4a52348 (cherry picked from commit 1f47e1c22ef82e725575666ddff943c1d560652a) --- Tethering/AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tethering/AndroidManifest.xml b/Tethering/AndroidManifest.xml index c71d0d7bc5..9328611f5d 100644 --- a/Tethering/AndroidManifest.xml +++ b/Tethering/AndroidManifest.xml @@ -27,7 +27,7 @@ added to the privileged permissions whitelist for that package. --> - + From 299d3a740fb75bad24f5764f95f5fe7488e50d34 Mon Sep 17 00:00:00 2001 From: markchien Date: Thu, 12 Mar 2020 15:22:01 +0800 Subject: [PATCH 068/188] Give tethering bluetooth privilege permission Permisssion of PanService#setBluetoothTethering is change from BLUETOOTH_ADMIN to BLUETOOTH_PRIVILEGED. Tethering service need bluetooth privilege permission to enable bluetooth tethering. Bug: 146045934 Test: on/off bluetooth tethering Merged-In: Ib87a5d5a5bb49390aa55e52713bb3539d4a52348 Change-Id: Ib87a5d5a5bb49390aa55e52713bb3539d4a52348 (cherry picked from commit 1f47e1c22ef82e725575666ddff943c1d560652a) --- Tethering/AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tethering/AndroidManifest.xml b/Tethering/AndroidManifest.xml index c71d0d7bc5..9328611f5d 100644 --- a/Tethering/AndroidManifest.xml +++ b/Tethering/AndroidManifest.xml @@ -27,7 +27,7 @@ added to the privileged permissions whitelist for that package. --> - + From 7939cb0a4a8b7a167e19c64238ebfcbbc2245f9e Mon Sep 17 00:00:00 2001 From: Anton Hansson Date: Tue, 24 Mar 2020 19:52:44 +0000 Subject: [PATCH 069/188] Make framework-tethering stubs use the new defaults Makes it convenient to change all stubs from a central place. Bug: 149293194 Test: m framework-tethering-stubs{public,system,module_libs_}api Change-Id: I330133824e78b3a8927e3d3ffbbd729bcdcb8822 --- Tethering/common/TetheringLib/Android.bp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Tethering/common/TetheringLib/Android.bp b/Tethering/common/TetheringLib/Android.bp index 2fbba68f1e..9aeffc0dbf 100644 --- a/Tethering/common/TetheringLib/Android.bp +++ b/Tethering/common/TetheringLib/Android.bp @@ -125,17 +125,17 @@ droidstubs { java_library { name: "framework-tethering-stubs-publicapi", srcs: [":framework-tethering-stubs-srcs-publicapi"], - sdk_version: "current", + defaults: ["framework-module-stubs-lib-defaults-publicapi"], } java_library { name: "framework-tethering-stubs-systemapi", srcs: [":framework-tethering-stubs-srcs-systemapi"], - sdk_version: "system_current", + defaults: ["framework-module-stubs-lib-defaults-systemapi"], } java_library { name: "framework-tethering-stubs-module_libs_api", srcs: [":framework-tethering-stubs-srcs-module_libs_api"], - sdk_version: "module_current", + defaults: ["framework-module-stubs-lib-defaults-systemapi"], } From 577707e09b2e39a019368c8be3383b2c580c2d92 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Wed, 25 Mar 2020 11:19:36 +0000 Subject: [PATCH 070/188] Add permitted_packages to framework-tethering. Test: m out/soong/.intermediates/frameworks/base/packages/Tethering/common/TetheringLib/framework-tethering/android_common/package-check.stamp Bug: 151314205 Change-Id: I248c36b2cf2f5776978c4fd2322d3b73ade309ff --- Tethering/common/TetheringLib/Android.bp | 1 + 1 file changed, 1 insertion(+) diff --git a/Tethering/common/TetheringLib/Android.bp b/Tethering/common/TetheringLib/Android.bp index 2fbba68f1e..00d0d9c428 100644 --- a/Tethering/common/TetheringLib/Android.bp +++ b/Tethering/common/TetheringLib/Android.bp @@ -60,6 +60,7 @@ java_library { hostdex: true, // for hiddenapi check visibility: ["//frameworks/base/packages/Tethering:__subpackages__"], apex_available: ["com.android.tethering"], + permitted_packages: ["android.net"], } stubs_defaults { From e4beb931497ec7b602e855afda168a68ba1a3e7c Mon Sep 17 00:00:00 2001 From: Paul Hu Date: Wed, 25 Mar 2020 06:22:43 +0000 Subject: [PATCH 071/188] [TNU02] Update tethering notification by active data subid Tethering notification can be customized by different subid. Thus update notification when active data subid changed. Bug: 122085773 Bug: 130596698 Test: atest TetheringTests Change-Id: I799d713326cfbf4dc96c712c6b15ed5a4ac18dd2 Merged-In: I799d713326cfbf4dc96c712c6b15ed5a4ac18dd2 (cherry picked from aosp/1209984) --- .../connectivity/tethering/Tethering.java | 3 +- .../TetheringNotificationUpdater.java | 42 +++++++++++++++---- .../connectivity/tethering/TetheringTest.java | 2 +- 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java index 36113acf97..490b83f48e 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java +++ b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java @@ -369,9 +369,10 @@ public class Tethering { mActiveDataSubId = subId; updateConfiguration(); + mNotificationUpdater.onActiveDataSubscriptionIdChanged(subId); // To avoid launching unexpected provisioning checks, ignore re-provisioning // when no CarrierConfig loaded yet. Assume reevaluateSimCardProvisioning() - // ill be triggered again when CarrierConfig is loaded. + // will be triggered again when CarrierConfig is loaded. if (mEntitlementMgr.getCarrierConfig(mConfig) != null) { mEntitlementMgr.reevaluateSimCardProvisioning(mConfig); } else { diff --git a/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java b/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java index b97f75268a..c3fd170a11 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java +++ b/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java @@ -29,6 +29,7 @@ import android.content.Intent; import android.content.res.Resources; import android.os.UserHandle; import android.provider.Settings; +import android.telephony.SubscriptionManager; import android.text.TextUtils; import android.util.Log; import android.util.SparseArray; @@ -54,6 +55,9 @@ import com.android.networkstack.tethering.R; public class TetheringNotificationUpdater { private static final String TAG = TetheringNotificationUpdater.class.getSimpleName(); private static final String CHANNEL_ID = "TETHERING_STATUS"; + private static final String WIFI_DOWNSTREAM = "WIFI"; + private static final String USB_DOWNSTREAM = "USB"; + private static final String BLUETOOTH_DOWNSTREAM = "BT"; private static final boolean NOTIFY_DONE = true; private static final boolean NO_NOTIFY = false; // Id to update and cancel tethering notification. Must be unique within the tethering app. @@ -65,14 +69,22 @@ public class TetheringNotificationUpdater { private final Context mContext; private final NotificationManager mNotificationManager; private final NotificationChannel mChannel; - // Downstream type is one of ConnectivityManager.TETHERING_* constants, 0 1 or 2. - // This value has to be made 1 2 and 4, and OR'd with the others. + // WARNING : the constructor is called on a different thread. Thread safety therefore // relies on this value being initialized to 0, and not any other value. If you need // to change this, you will need to change the thread where the constructor is invoked, // or to introduce synchronization. + // Downstream type is one of ConnectivityManager.TETHERING_* constants, 0 1 or 2. + // This value has to be made 1 2 and 4, and OR'd with the others. private int mDownstreamTypesMask = DOWNSTREAM_NONE; + // WARNING : this value is not able to being initialized to 0 and must have volatile because + // telephony service is not guaranteed that is up before tethering service starts. If telephony + // is up later than tethering, TetheringNotificationUpdater will use incorrect and valid + // subscription id(0) to query resources. Therefore, initialized subscription id must be + // INVALID_SUBSCRIPTION_ID. + private volatile int mActiveDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + public TetheringNotificationUpdater(@NonNull final Context context) { mContext = context; mNotificationManager = (NotificationManager) context.createContextAsUser(UserHandle.ALL, 0) @@ -91,6 +103,18 @@ public class TetheringNotificationUpdater { updateNotification(); } + /** Called when active data subscription id changed */ + public void onActiveDataSubscriptionIdChanged(final int subId) { + if (mActiveDataSubId == subId) return; + mActiveDataSubId = subId; + updateNotification(); + } + + @VisibleForTesting + Resources getResourcesForSubId(@NonNull final Context c, final int subId) { + return SubscriptionManager.getResourcesForSubId(c, subId); + } + private void updateNotification() { final boolean tetheringInactive = mDownstreamTypesMask <= DOWNSTREAM_NONE; @@ -115,11 +139,11 @@ public class TetheringNotificationUpdater { int downstreamTypesMask = DOWNSTREAM_NONE; final String[] downstreams = types.split("\\|"); for (String downstream : downstreams) { - if ("USB".equals(downstream.trim())) { + if (USB_DOWNSTREAM.equals(downstream.trim())) { downstreamTypesMask |= (1 << TETHERING_USB); - } else if ("WIFI".equals(downstream.trim())) { + } else if (WIFI_DOWNSTREAM.equals(downstream.trim())) { downstreamTypesMask |= (1 << TETHERING_WIFI); - } else if ("BT".equals(downstream.trim())) { + } else if (BLUETOOTH_DOWNSTREAM.equals(downstream.trim())) { downstreamTypesMask |= (1 << TETHERING_BLUETOOTH); } } @@ -135,8 +159,7 @@ public class TetheringNotificationUpdater { * @return {@link android.util.SparseArray} with downstream types and icon id info. */ @NonNull - private SparseArray getIcons(@ArrayRes int id) { - final Resources res = mContext.getResources(); + private SparseArray getIcons(@ArrayRes int id, @NonNull Resources res) { final String[] array = res.getStringArray(id); final SparseArray icons = new SparseArray<>(); for (String config : array) { @@ -161,8 +184,9 @@ public class TetheringNotificationUpdater { } private boolean setupNotification() { - final Resources res = mContext.getResources(); - final SparseArray downstreamIcons = getIcons(R.array.tethering_notification_icons); + final Resources res = getResourcesForSubId(mContext, mActiveDataSubId); + final SparseArray downstreamIcons = + getIcons(R.array.tethering_notification_icons, res); final int iconId = downstreamIcons.get(mDownstreamTypesMask, NO_ICON_ID); if (iconId == NO_ICON_ID) return NO_NOTIFY; diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java index 60d7ad1c5b..8116f9d74b 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java @@ -210,7 +210,6 @@ public class TetheringTest { private PhoneStateListener mPhoneStateListener; private InterfaceConfigurationParcel mInterfaceConfiguration; - private class TestContext extends BroadcastInterceptingContext { TestContext(Context base) { super(base); @@ -1399,6 +1398,7 @@ public class TetheringTest { mPhoneStateListener.onActiveDataSubscriptionIdChanged(fakeSubId); final TetheringConfiguration newConfig = mTethering.getTetheringConfiguration(); assertEquals(fakeSubId, newConfig.activeDataSubId); + verify(mNotificationUpdater, times(1)).onActiveDataSubscriptionIdChanged(eq(fakeSubId)); } @Test From 71007d31b5804056f3a12e77f4338cfe8140ad4c Mon Sep 17 00:00:00 2001 From: Paul Hu Date: Wed, 25 Mar 2020 07:53:03 +0000 Subject: [PATCH 072/188] [TNU03] Add TetheringNotificationUpdaterTest Add new test for TetheringNotificationUpdater Bug: 122085773 Bug: 130596698 Test: atest TetheringTests Change-Id: I0db3df3e85dd6a8c3989c8bc66a06c50f45a0c15 Merged-In: I0db3df3e85dd6a8c3989c8bc66a06c50f45a0c15 (cherry picked from aosp/1209985) --- .../TetheringNotificationUpdater.java | 7 +- .../TetheringNotificationUpdaterTest.kt | 227 ++++++++++++++++++ 2 files changed, 231 insertions(+), 3 deletions(-) create mode 100644 Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringNotificationUpdaterTest.kt diff --git a/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java b/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java index c3fd170a11..b3fff4465c 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java +++ b/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java @@ -134,8 +134,9 @@ public class TetheringNotificationUpdater { * * @return downstream types mask value. */ + @VisibleForTesting @IntRange(from = 0, to = 7) - private int getDownstreamTypesMask(@NonNull final String types) { + int getDownstreamTypesMask(@NonNull final String types) { int downstreamTypesMask = DOWNSTREAM_NONE; final String[] downstreams = types.split("\\|"); for (String downstream : downstreams) { @@ -158,8 +159,8 @@ public class TetheringNotificationUpdater { * * @return {@link android.util.SparseArray} with downstream types and icon id info. */ - @NonNull - private SparseArray getIcons(@ArrayRes int id, @NonNull Resources res) { + @VisibleForTesting + SparseArray getIcons(@ArrayRes int id, @NonNull Resources res) { final String[] array = res.getStringArray(id); final SparseArray icons = new SparseArray<>(); for (String config : array) { diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringNotificationUpdaterTest.kt b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringNotificationUpdaterTest.kt new file mode 100644 index 0000000000..124f9f4806 --- /dev/null +++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringNotificationUpdaterTest.kt @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.connectivity.tethering + +import android.app.Notification +import android.app.NotificationManager +import android.content.Context +import android.content.res.Resources +import android.net.ConnectivityManager.TETHERING_BLUETOOTH +import android.net.ConnectivityManager.TETHERING_USB +import android.net.ConnectivityManager.TETHERING_WIFI +import android.os.UserHandle +import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID +import androidx.test.InstrumentationRegistry +import androidx.test.filters.SmallTest +import androidx.test.runner.AndroidJUnit4 +import com.android.internal.util.test.BroadcastInterceptingContext +import com.android.networkstack.tethering.R +import com.android.server.connectivity.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers.any +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.ArgumentMatchers.eq +import org.mockito.Mock +import org.mockito.Mockito.doReturn +import org.mockito.Mockito.never +import org.mockito.Mockito.reset +import org.mockito.Mockito.times +import org.mockito.Mockito.verifyZeroInteractions +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +const val TEST_SUBID = 1 +const val WIFI_ICON_ID = 1 +const val USB_ICON_ID = 2 +const val BT_ICON_ID = 3 +const val GENERAL_ICON_ID = 4 +const val WIFI_MASK = 1 shl TETHERING_WIFI +const val USB_MASK = 1 shl TETHERING_USB +const val BT_MASK = 1 shl TETHERING_BLUETOOTH +const val TITTLE = "Tethering active" +const val MESSAGE = "Tap here to set up." +const val TEST_TITTLE = "Hotspot active" +const val TEST_MESSAGE = "Tap to set up hotspot." + +@RunWith(AndroidJUnit4::class) +@SmallTest +class TetheringNotificationUpdaterTest { + // lateinit used here for mocks as they need to be reinitialized between each test and the test + // should crash if they are used before being initialized. + @Mock private lateinit var mockContext: Context + @Mock private lateinit var notificationManager: NotificationManager + @Mock private lateinit var defaultResources: Resources + @Mock private lateinit var testResources: Resources + + // lateinit for this class under test, as it should be reset to a different instance for every + // tests but should always be initialized before use (or the test should crash). + private lateinit var notificationUpdater: TetheringNotificationUpdater + + private val ENABLE_ICON_CONFIGS = arrayOf( + "USB;android.test:drawable/usb", "BT;android.test:drawable/bluetooth", + "WIFI|BT;android.test:drawable/general", "WIFI|USB;android.test:drawable/general", + "USB|BT;android.test:drawable/general", "WIFI|USB|BT;android.test:drawable/general") + + private inner class TestContext(c: Context) : BroadcastInterceptingContext(c) { + override fun createContextAsUser(user: UserHandle, flags: Int) = + if (user == UserHandle.ALL) mockContext else this + } + + private inner class WrappedNotificationUpdater(c: Context) : TetheringNotificationUpdater(c) { + override fun getResourcesForSubId(context: Context, subId: Int) = + if (subId == TEST_SUBID) testResources else defaultResources + } + + private fun setupResources() { + doReturn(ENABLE_ICON_CONFIGS).`when`(defaultResources) + .getStringArray(R.array.tethering_notification_icons) + doReturn(arrayOf("WIFI;android.test:drawable/wifi")).`when`(testResources) + .getStringArray(R.array.tethering_notification_icons) + doReturn(TITTLE).`when`(defaultResources).getString(R.string.tethering_notification_title) + doReturn(MESSAGE).`when`(defaultResources) + .getString(R.string.tethering_notification_message) + doReturn(TEST_TITTLE).`when`(testResources).getString(R.string.tethering_notification_title) + doReturn(TEST_MESSAGE).`when`(testResources) + .getString(R.string.tethering_notification_message) + doReturn(USB_ICON_ID).`when`(defaultResources) + .getIdentifier(eq("android.test:drawable/usb"), any(), any()) + doReturn(BT_ICON_ID).`when`(defaultResources) + .getIdentifier(eq("android.test:drawable/bluetooth"), any(), any()) + doReturn(GENERAL_ICON_ID).`when`(defaultResources) + .getIdentifier(eq("android.test:drawable/general"), any(), any()) + doReturn(WIFI_ICON_ID).`when`(testResources) + .getIdentifier(eq("android.test:drawable/wifi"), any(), any()) + } + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + val context = TestContext(InstrumentationRegistry.getContext()) + doReturn(notificationManager).`when`(mockContext) + .getSystemService(Context.NOTIFICATION_SERVICE) + notificationUpdater = WrappedNotificationUpdater(context) + setupResources() + } + + private fun Notification.title() = this.extras.getString(Notification.EXTRA_TITLE) + private fun Notification.text() = this.extras.getString(Notification.EXTRA_TEXT) + + private fun verifyNotification(iconId: Int = 0, title: String = "", text: String = "") { + verify(notificationManager, never()).cancel(any(), anyInt()) + + val notificationCaptor = ArgumentCaptor.forClass(Notification::class.java) + verify(notificationManager, times(1)).notify(any(), anyInt(), notificationCaptor.capture()) + + val notification = notificationCaptor.getValue() + assertEquals(iconId, notification.smallIcon.resId) + assertEquals(title, notification.title()) + assertEquals(text, notification.text()) + + reset(notificationManager) + } + + private fun verifyNoNotification() { + verify(notificationManager, times(1)).cancel(any(), anyInt()) + verify(notificationManager, never()).notify(any(), anyInt(), any()) + + reset(notificationManager) + } + + @Test + fun testNotificationWithDownstreamChanged() { + // Wifi downstream. No notification. + notificationUpdater.onDownstreamChanged(WIFI_MASK) + verifyNoNotification() + + // Same downstream changed. Nothing happened. + notificationUpdater.onDownstreamChanged(WIFI_MASK) + verifyZeroInteractions(notificationManager) + + // Wifi and usb downstreams. Show enable notification + notificationUpdater.onDownstreamChanged(WIFI_MASK or USB_MASK) + verifyNotification(GENERAL_ICON_ID, TITTLE, MESSAGE) + + // Usb downstream. Still show enable notification. + notificationUpdater.onDownstreamChanged(USB_MASK) + verifyNotification(USB_ICON_ID, TITTLE, MESSAGE) + + // No downstream. No notification. + notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE) + verifyNoNotification() + } + + @Test + fun testNotificationWithActiveDataSubscriptionIdChanged() { + // Usb downstream. Showed enable notification with default resource. + notificationUpdater.onDownstreamChanged(USB_MASK) + verifyNotification(USB_ICON_ID, TITTLE, MESSAGE) + + // Same subId changed. Nothing happened. + notificationUpdater.onActiveDataSubscriptionIdChanged(INVALID_SUBSCRIPTION_ID) + verifyZeroInteractions(notificationManager) + + // Set test sub id. Clear notification with test resource. + notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID) + verifyNoNotification() + + // Wifi downstream. Show enable notification with test resource. + notificationUpdater.onDownstreamChanged(WIFI_MASK) + verifyNotification(WIFI_ICON_ID, TEST_TITTLE, TEST_MESSAGE) + + // No downstream. No notification. + notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE) + verifyNoNotification() + } + + private fun assertIconNumbers(number: Int, configs: Array) { + doReturn(configs).`when`(defaultResources) + .getStringArray(R.array.tethering_notification_icons) + assertEquals(number, notificationUpdater.getIcons( + R.array.tethering_notification_icons, defaultResources).size()) + } + + @Test + fun testGetIcons() { + assertIconNumbers(0, arrayOfNulls(0)) + assertIconNumbers(0, arrayOf(null, "")) + assertIconNumbers(3, arrayOf( + // These configurations are invalid with wrong strings or symbols. + ";", ",", "|", "|,;", "WIFI", "1;2", " U SB; ", "bt;", "WIFI;USB;BT", "WIFI|USB|BT", + "WIFI,BT,USB", " WIFI| | | USB, test:drawable/test", + // This configuration is valid with two downstream types (USB, BT). + "USB|,,,,,|BT;drawable/test ", + // This configuration is valid with one downstream types (WIFI). + " WIFI ; android.test:drawable/xxx ")) + } + + @Test + fun testGetDownstreamTypesMask() { + assertEquals(DOWNSTREAM_NONE, notificationUpdater.getDownstreamTypesMask("")) + assertEquals(DOWNSTREAM_NONE, notificationUpdater.getDownstreamTypesMask("1")) + assertEquals(DOWNSTREAM_NONE, notificationUpdater.getDownstreamTypesMask("WIFI_P2P")) + assertEquals(DOWNSTREAM_NONE, notificationUpdater.getDownstreamTypesMask("usb")) + assertEquals(WIFI_MASK, notificationUpdater.getDownstreamTypesMask(" WIFI ")) + assertEquals(USB_MASK, notificationUpdater.getDownstreamTypesMask("USB | B T")) + assertEquals(BT_MASK, notificationUpdater.getDownstreamTypesMask(" WIFI: | BT")) + assertEquals(WIFI_MASK or USB_MASK, + notificationUpdater.getDownstreamTypesMask("1|2|USB|WIFI|BLUETOOTH||")) + } +} \ No newline at end of file From fe6c41c81517c6b1146c5aa58f8a3129e0d2ef37 Mon Sep 17 00:00:00 2001 From: Paul Hu Date: Wed, 25 Mar 2020 12:39:13 +0000 Subject: [PATCH 073/188] [TNU04] Add tethering restricted notification If tethering is restricted to the user, show restricted notification to notify the user. Bug: 122085773 Test: atest TetheringTests Change-Id: Ic5baca2d6102886f4c3530ce1e321b5dab6ea9d7 Merged-In: Ic5baca2d6102886f4c3530ce1e321b5dab6ea9d7 (cherry picked from aosp/1188867) --- .../connectivity/tethering/Tethering.java | 28 +++++++++---- .../TetheringNotificationUpdater.java | 41 ++++++++++++++----- .../TetheringNotificationUpdaterTest.kt | 41 +++++++++++++++++-- .../connectivity/tethering/TetheringTest.java | 10 +++-- 4 files changed, 95 insertions(+), 25 deletions(-) diff --git a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java index 490b83f48e..77ff61791c 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java +++ b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java @@ -303,7 +303,8 @@ public class Tethering { final UserManager userManager = (UserManager) mContext.getSystemService( Context.USER_SERVICE); - mTetheringRestriction = new UserRestrictionActionListener(userManager, this); + mTetheringRestriction = new UserRestrictionActionListener( + userManager, this, mNotificationUpdater); mExecutor = new TetheringThreadExecutor(mHandler); mActiveDataSubIdListener = new ActiveDataSubIdListener(mExecutor); @@ -997,11 +998,14 @@ public class Tethering { protected static class UserRestrictionActionListener { private final UserManager mUserManager; private final Tethering mWrapper; + private final TetheringNotificationUpdater mNotificationUpdater; public boolean mDisallowTethering; - public UserRestrictionActionListener(UserManager um, Tethering wrapper) { + public UserRestrictionActionListener(@NonNull UserManager um, @NonNull Tethering wrapper, + @NonNull TetheringNotificationUpdater updater) { mUserManager = um; mWrapper = wrapper; + mNotificationUpdater = updater; mDisallowTethering = false; } @@ -1020,13 +1024,21 @@ public class Tethering { return; } - // TODO: Add user restrictions notification. - final boolean isTetheringActiveOnDevice = (mWrapper.getTetheredIfaces().length != 0); - - if (newlyDisallowed && isTetheringActiveOnDevice) { - mWrapper.untetherAll(); - // TODO(b/148139325): send tetheringSupported on restriction change + if (!newlyDisallowed) { + // Clear the restricted notification when user is allowed to have tethering + // function. + mNotificationUpdater.tetheringRestrictionLifted(); + return; } + + // Restricted notification is shown when tethering function is disallowed on + // user's device. + mNotificationUpdater.notifyTetheringDisabledByRestriction(); + + // Untether from all downstreams since tethering is disallowed. + mWrapper.untetherAll(); + + // TODO(b/148139325): send tetheringSupported on restriction change } } diff --git a/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java b/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java index b3fff4465c..992cdd8de6 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java +++ b/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java @@ -36,6 +36,7 @@ import android.util.SparseArray; import androidx.annotation.ArrayRes; import androidx.annotation.DrawableRes; +import androidx.annotation.IntDef; import androidx.annotation.IntRange; import androidx.annotation.NonNull; @@ -61,7 +62,9 @@ public class TetheringNotificationUpdater { private static final boolean NOTIFY_DONE = true; private static final boolean NO_NOTIFY = false; // Id to update and cancel tethering notification. Must be unique within the tethering app. - private static final int NOTIFY_ID = 20191115; + private static final int ENABLE_NOTIFICATION_ID = 1000; + // Id to update and cancel restricted notification. Must be unique within the tethering app. + private static final int RESTRICTED_NOTIFICATION_ID = 1001; @VisibleForTesting static final int NO_ICON_ID = 0; @VisibleForTesting @@ -85,6 +88,9 @@ public class TetheringNotificationUpdater { // INVALID_SUBSCRIPTION_ID. private volatile int mActiveDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + @IntDef({ENABLE_NOTIFICATION_ID, RESTRICTED_NOTIFICATION_ID}) + @interface NotificationId {} + public TetheringNotificationUpdater(@NonNull final Context context) { mContext = context; mNotificationManager = (NotificationManager) context.createContextAsUser(UserHandle.ALL, 0) @@ -100,14 +106,14 @@ public class TetheringNotificationUpdater { public void onDownstreamChanged(@IntRange(from = 0, to = 7) final int downstreamTypesMask) { if (mDownstreamTypesMask == downstreamTypesMask) return; mDownstreamTypesMask = downstreamTypesMask; - updateNotification(); + updateEnableNotification(); } /** Called when active data subscription id changed */ public void onActiveDataSubscriptionIdChanged(final int subId) { if (mActiveDataSubId == subId) return; mActiveDataSubId = subId; - updateNotification(); + updateEnableNotification(); } @VisibleForTesting @@ -115,16 +121,31 @@ public class TetheringNotificationUpdater { return SubscriptionManager.getResourcesForSubId(c, subId); } - private void updateNotification() { + private void updateEnableNotification() { final boolean tetheringInactive = mDownstreamTypesMask <= DOWNSTREAM_NONE; if (tetheringInactive || setupNotification() == NO_NOTIFY) { - clearNotification(); + clearNotification(ENABLE_NOTIFICATION_ID); } } - private void clearNotification() { - mNotificationManager.cancel(null /* tag */, NOTIFY_ID); + @VisibleForTesting + void tetheringRestrictionLifted() { + clearNotification(RESTRICTED_NOTIFICATION_ID); + } + + private void clearNotification(@NotificationId final int id) { + mNotificationManager.cancel(null /* tag */, id); + } + + @VisibleForTesting + void notifyTetheringDisabledByRestriction() { + final Resources res = getResourcesForSubId(mContext, mActiveDataSubId); + final String title = res.getString(R.string.disable_tether_notification_title); + final String message = res.getString(R.string.disable_tether_notification_message); + + showNotification(R.drawable.stat_sys_tether_general, title, message, + RESTRICTED_NOTIFICATION_ID); } /** @@ -195,12 +216,12 @@ public class TetheringNotificationUpdater { final String title = res.getString(R.string.tethering_notification_title); final String message = res.getString(R.string.tethering_notification_message); - showNotification(iconId, title, message); + showNotification(iconId, title, message, ENABLE_NOTIFICATION_ID); return NOTIFY_DONE; } private void showNotification(@DrawableRes final int iconId, @NonNull final String title, - @NonNull final String message) { + @NonNull final String message, @NotificationId final int id) { final Intent intent = new Intent(Settings.ACTION_TETHER_SETTINGS); final PendingIntent pi = PendingIntent.getActivity( mContext.createContextAsUser(UserHandle.CURRENT, 0), @@ -218,6 +239,6 @@ public class TetheringNotificationUpdater { .setContentIntent(pi) .build(); - mNotificationManager.notify(null /* tag */, NOTIFY_ID, notification); + mNotificationManager.notify(null /* tag */, id, notification); } } diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringNotificationUpdaterTest.kt b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringNotificationUpdaterTest.kt index 124f9f4806..b86949185c 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringNotificationUpdaterTest.kt +++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringNotificationUpdaterTest.kt @@ -25,7 +25,7 @@ import android.net.ConnectivityManager.TETHERING_USB import android.net.ConnectivityManager.TETHERING_WIFI import android.os.UserHandle import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID -import androidx.test.InstrumentationRegistry +import androidx.test.platform.app.InstrumentationRegistry import androidx.test.filters.SmallTest import androidx.test.runner.AndroidJUnit4 import com.android.internal.util.test.BroadcastInterceptingContext @@ -114,7 +114,7 @@ class TetheringNotificationUpdaterTest { @Before fun setUp() { MockitoAnnotations.initMocks(this) - val context = TestContext(InstrumentationRegistry.getContext()) + val context = TestContext(InstrumentationRegistry.getInstrumentation().context) doReturn(notificationManager).`when`(mockContext) .getSystemService(Context.NOTIFICATION_SERVICE) notificationUpdater = WrappedNotificationUpdater(context) @@ -128,7 +128,8 @@ class TetheringNotificationUpdaterTest { verify(notificationManager, never()).cancel(any(), anyInt()) val notificationCaptor = ArgumentCaptor.forClass(Notification::class.java) - verify(notificationManager, times(1)).notify(any(), anyInt(), notificationCaptor.capture()) + verify(notificationManager, times(1)) + .notify(any(), anyInt(), notificationCaptor.capture()) val notification = notificationCaptor.getValue() assertEquals(iconId, notification.smallIcon.resId) @@ -224,4 +225,38 @@ class TetheringNotificationUpdaterTest { assertEquals(WIFI_MASK or USB_MASK, notificationUpdater.getDownstreamTypesMask("1|2|USB|WIFI|BLUETOOTH||")) } + + @Test + fun testSetupRestrictedNotification() { + val title = InstrumentationRegistry.getInstrumentation().context.resources + .getString(R.string.disable_tether_notification_title) + val message = InstrumentationRegistry.getInstrumentation().context.resources + .getString(R.string.disable_tether_notification_message) + val disallowTitle = "Tether function is disallowed" + val disallowMessage = "Please contact your admin" + doReturn(title).`when`(defaultResources) + .getString(R.string.disable_tether_notification_title) + doReturn(message).`when`(defaultResources) + .getString(R.string.disable_tether_notification_message) + doReturn(disallowTitle).`when`(testResources) + .getString(R.string.disable_tether_notification_title) + doReturn(disallowMessage).`when`(testResources) + .getString(R.string.disable_tether_notification_message) + + // User restrictions on. Show restricted notification. + notificationUpdater.notifyTetheringDisabledByRestriction() + verifyNotification(R.drawable.stat_sys_tether_general, title, message) + + // User restrictions off. Clear notification. + notificationUpdater.tetheringRestrictionLifted() + verifyNoNotification() + + // Set test sub id. No notification. + notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID) + verifyNoNotification() + + // User restrictions on again. Show restricted notification with test resource. + notificationUpdater.notifyTetheringDisabledByRestriction() + verifyNotification(R.drawable.stat_sys_tether_general, disallowTitle, disallowMessage) + } } \ No newline at end of file diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java index 8116f9d74b..5ead1106d7 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java @@ -1072,13 +1072,15 @@ public class TetheringTest { when(mUserManager.getUserRestrictions()).thenReturn(newRestrictions); final Tethering.UserRestrictionActionListener ural = - new Tethering.UserRestrictionActionListener(mUserManager, mockTethering); + new Tethering.UserRestrictionActionListener( + mUserManager, mockTethering, mNotificationUpdater); ural.mDisallowTethering = currentDisallow; ural.onUserRestrictionsChanged(); - verify(mockTethering, times(expectedInteractionsWithShowNotification)) - .untetherAll(); + verify(mNotificationUpdater, times(expectedInteractionsWithShowNotification)) + .notifyTetheringDisabledByRestriction(); + verify(mockTethering, times(expectedInteractionsWithShowNotification)).untetherAll(); } @Test @@ -1086,7 +1088,7 @@ public class TetheringTest { final String[] emptyActiveIfacesList = new String[]{}; final boolean currDisallow = false; final boolean nextDisallow = true; - final int expectedInteractionsWithShowNotification = 0; + final int expectedInteractionsWithShowNotification = 1; runUserRestrictionsChange(currDisallow, nextDisallow, emptyActiveIfacesList, expectedInteractionsWithShowNotification); From 595266e4a861fc32e9cc913660f14ec57ba6565e Mon Sep 17 00:00:00 2001 From: Treehugger Robot Date: Tue, 24 Mar 2020 15:23:51 +0000 Subject: [PATCH 074/188] Add a log message if enabling Ethernet tethering if it is already enabled. Test: builds Bug: 150644681 Change-Id: I68123e6dd04ccae5da2ecd7526c11d9f835d1d57 Merged-In: I68123e6dd04ccae5da2ecd7526c11d9f835d1d57 (cherry picked from commit e6b8ded8121c137a7e434e1c2a9e04cc144f2f28) --- .../com/android/server/connectivity/tethering/Tethering.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java index 77ff61791c..3624dc7087 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java +++ b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java @@ -637,7 +637,10 @@ public class Tethering { Context.ETHERNET_SERVICE); synchronized (mPublicSync) { if (enable) { - if (mEthernetCallback != null) return TETHER_ERROR_NO_ERROR; + if (mEthernetCallback != null) { + Log.d(TAG, "Ethernet tethering already started"); + return TETHER_ERROR_NO_ERROR; + } mEthernetCallback = new EthernetCallback(); mEthernetIfaceRequest = em.requestTetheredInterface(mExecutor, mEthernetCallback); From a4e2383d843d27012c5f60123d8041640f07ea57 Mon Sep 17 00:00:00 2001 From: Treehugger Robot Date: Wed, 25 Mar 2020 15:32:41 +0000 Subject: [PATCH 075/188] Update connected clients when a downstream disappears. Otherwise, if another downstream of the same type reappears, the code would fire a callback with the previous list of clients. Bug: 150644681 Test: atest TetheringIntegrationTests:EthernetTetheringTest --rerun-until-failure 100 Change-Id: I6b34ea747ae1831001077f44879bb6828dcecc96 Merged-In: I6b34ea747ae1831001077f44879bb6828dcecc96 (cherry picked from commit 3984360f642ddd5820ced5a6935e37a8ae0d9d76) --- .../server/connectivity/tethering/Tethering.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java index 3624dc7087..c84892d675 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java +++ b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java @@ -433,9 +433,7 @@ public class Tethering { // Called by wifi when the number of soft AP clients changed. @Override public void onConnectedClientsChanged(final List clients) { - if (mConnectedClientsTracker.updateConnectedClients(mForwardedDownstreams, clients)) { - reportTetherClientsChanged(mConnectedClientsTracker.getLastTetheredClients()); - } + updateConnectedClients(clients); } } @@ -1575,6 +1573,7 @@ public class Tethering { mIPv6TetheringCoordinator.removeActiveDownstream(who); mOffload.excludeDownstreamInterface(who.interfaceName()); mForwardedDownstreams.remove(who); + updateConnectedClients(null /* wifiClients */); // If this is a Wi-Fi interface, tell WifiManager of any errors // or the inactive serving state. @@ -2157,6 +2156,12 @@ public class Tethering { return false; } + private void updateConnectedClients(final List wifiClients) { + if (mConnectedClientsTracker.updateConnectedClients(mForwardedDownstreams, wifiClients)) { + reportTetherClientsChanged(mConnectedClientsTracker.getLastTetheredClients()); + } + } + private IpServer.Callback makeControlCallback() { return new IpServer.Callback() { @Override @@ -2171,10 +2176,7 @@ public class Tethering { @Override public void dhcpLeasesChanged() { - if (mConnectedClientsTracker.updateConnectedClients( - mForwardedDownstreams, null /* wifiClients */)) { - reportTetherClientsChanged(mConnectedClientsTracker.getLastTetheredClients()); - } + updateConnectedClients(null /* wifiClients */); } }; } From eb733107272d3ac6767a7de31a7e6be062e6a939 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Fri, 27 Mar 2020 11:07:01 +0000 Subject: [PATCH 076/188] Add an Ethernet tethering test. This test uses EthernetManager's ability to use test network interfaces to test tethering. This provides a fairly realistic integration test for Tethering and its callbacks, IpServer and DhcpServer, and so on. It is in a new integration/ directory under Tethering because I didn't really know where to put it. It's fast enough to run in presubmit, but it didn't seem to be appropriate to call it a unit test, and in the future we could also use this test to do some limited testing of real Ethernet tethering as well. Bug: 150644681 Test: atest TetheringIntegrationTests:EthernetTetheringTest --rerun-until-failure 100 Merged-In: Ifcda70b73848b1fd4c26b031e53f0a6500cc93d4 Change-Id: Ifcda70b73848b1fd4c26b031e53f0a6500cc93d4 --- Tethering/tests/integration/Android.bp | 42 ++ .../tests/integration/AndroidManifest.xml | 29 ++ .../android/net/EthernetTetheringTest.java | 460 ++++++++++++++++++ 3 files changed, 531 insertions(+) create mode 100644 Tethering/tests/integration/Android.bp create mode 100644 Tethering/tests/integration/AndroidManifest.xml create mode 100644 Tethering/tests/integration/src/android/net/EthernetTetheringTest.java diff --git a/Tethering/tests/integration/Android.bp b/Tethering/tests/integration/Android.bp new file mode 100644 index 0000000000..1a1c30d1d5 --- /dev/null +++ b/Tethering/tests/integration/Android.bp @@ -0,0 +1,42 @@ +// +// Copyright (C) 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +android_test { + name: "TetheringIntegrationTests", + certificate: "platform", + platform_apis: true, + srcs: [ + "src/**/*.java", + "src/**/*.kt", + ], + test_suites: [ + "device-tests", + "mts", + ], + static_libs: [ + "NetworkStackApiStableLib", + "androidx.test.rules", + "frameworks-base-testutils", + "mockito-target-extended-minus-junit4", + "net-tests-utils", + "testables", + ], + libs: [ + "android.test.runner", + "android.test.base", + "android.test.mock", + ], +} diff --git a/Tethering/tests/integration/AndroidManifest.xml b/Tethering/tests/integration/AndroidManifest.xml new file mode 100644 index 0000000000..233ba40b5d --- /dev/null +++ b/Tethering/tests/integration/AndroidManifest.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + diff --git a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java new file mode 100644 index 0000000000..843a4f19c3 --- /dev/null +++ b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java @@ -0,0 +1,460 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +import static android.Manifest.permission.MANAGE_TEST_NETWORKS; +import static android.Manifest.permission.NETWORK_SETTINGS; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeFalse; +import static org.junit.Assume.assumeTrue; + +import android.app.UiAutomation; +import android.content.Context; +import android.net.EthernetManager.TetheredInterfaceCallback; +import android.net.EthernetManager.TetheredInterfaceRequest; +import android.net.TetheringManager.StartTetheringCallback; +import android.net.TetheringManager.TetheringEventCallback; +import android.net.TetheringManager.TetheringRequest; +import android.net.dhcp.DhcpAckPacket; +import android.net.dhcp.DhcpOfferPacket; +import android.net.dhcp.DhcpPacket; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.SystemClock; +import android.system.Os; +import android.util.Log; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.MediumTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.testutils.HandlerUtilsKt; +import com.android.testutils.TapPacketReader; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.FileDescriptor; +import java.net.Inet4Address; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.nio.ByteBuffer; +import java.util.Collection; +import java.util.List; +import java.util.Random; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +@RunWith(AndroidJUnit4.class) +@MediumTest +public class EthernetTetheringTest { + + private static final String TAG = EthernetTetheringTest.class.getSimpleName(); + private static final int TIMEOUT_MS = 1000; + private static final int PACKET_READ_TIMEOUT_MS = 100; + private static final int DHCP_DISCOVER_ATTEMPTS = 10; + private static final byte[] DHCP_REQUESTED_PARAMS = new byte[] { + DhcpPacket.DHCP_SUBNET_MASK, + DhcpPacket.DHCP_ROUTER, + DhcpPacket.DHCP_DNS_SERVER, + DhcpPacket.DHCP_LEASE_TIME, + }; + private static final String DHCP_HOSTNAME = "testhostname"; + + private final Context mContext = InstrumentationRegistry.getContext(); + private final EthernetManager mEm = mContext.getSystemService(EthernetManager.class); + private final TetheringManager mTm = mContext.getSystemService(TetheringManager.class); + + private TestNetworkInterface mTestIface; + private HandlerThread mHandlerThread; + private Handler mHandler; + private TapPacketReader mTapPacketReader; + + private TetheredInterfaceRequester mTetheredInterfaceRequester; + private MyTetheringEventCallback mTetheringEventCallback; + + private UiAutomation mUiAutomation = + InstrumentationRegistry.getInstrumentation().getUiAutomation(); + + @Before + public void setUp() throws Exception { + mHandlerThread = new HandlerThread(getClass().getSimpleName()); + mHandlerThread.start(); + mHandler = new Handler(mHandlerThread.getLooper()); + mTetheredInterfaceRequester = new TetheredInterfaceRequester(mHandler, mEm); + // Needed to create a TestNetworkInterface, to call requestTetheredInterface, and to receive + // tethered client callbacks. + mUiAutomation.adoptShellPermissionIdentity(MANAGE_TEST_NETWORKS, NETWORK_SETTINGS); + } + + private void cleanUp() throws Exception { + mTm.stopTethering(TetheringManager.TETHERING_ETHERNET); + if (mTetheringEventCallback != null) { + mTetheringEventCallback.awaitInterfaceUntethered(); + mTetheringEventCallback.unregister(); + mTetheringEventCallback = null; + } + if (mTapPacketReader != null) { + TapPacketReader reader = mTapPacketReader; + mHandler.post(() -> reader.stop()); + mTapPacketReader = null; + } + mHandlerThread.quitSafely(); + mTetheredInterfaceRequester.release(); + mEm.setIncludeTestInterfaces(false); + maybeDeleteTestInterface(); + } + + @After + public void tearDown() throws Exception { + try { + cleanUp(); + } finally { + mUiAutomation.dropShellPermissionIdentity(); + } + } + + @Test + public void testVirtualEthernetAlreadyExists() throws Exception { + // This test requires manipulating packets. Skip if there is a physical Ethernet connected. + assumeFalse(mEm.isAvailable()); + + mTestIface = createTestInterface(); + // This must be done now because as soon as setIncludeTestInterfaces(true) is called, the + // interface will be placed in client mode, which will delete the link-local address. + // At that point NetworkInterface.getByName() will cease to work on the interface, because + // starting in R NetworkInterface can no longer see interfaces without IP addresses. + int mtu = getMTU(mTestIface); + + Log.d(TAG, "Including test interfaces"); + mEm.setIncludeTestInterfaces(true); + + Log.d(TAG, "Requesting tethered interface"); + mTetheredInterfaceRequester.requestInterface(); + + final String iface = mTetheredInterfaceRequester.awaitRequestedInterface(); + assertEquals("TetheredInterfaceCallback for unexpected interface", + mTestIface.getInterfaceName(), iface); + + checkVirtualEthernet(mTestIface, mtu); + } + + @Test + public void testVirtualEthernet() throws Exception { + // This test requires manipulating packets. Skip if there is a physical Ethernet connected. + assumeFalse(mEm.isAvailable()); + + Log.d(TAG, "Requesting tethered interface"); + mTetheredInterfaceRequester.requestInterface(); + + mEm.setIncludeTestInterfaces(true); + + mTestIface = createTestInterface(); + + final String iface = mTetheredInterfaceRequester.awaitRequestedInterface(); + assertEquals("TetheredInterfaceCallback for unexpected interface", + mTestIface.getInterfaceName(), iface); + + checkVirtualEthernet(mTestIface, getMTU(mTestIface)); + } + + @Test + public void testPhysicalEthernet() throws Exception { + assumeTrue(mEm.isAvailable()); + + // Get an interface to use. + mTetheredInterfaceRequester.requestInterface(); + String iface = mTetheredInterfaceRequester.awaitRequestedInterface(); + + // Enable Ethernet tethering and check that it starts. + mTetheringEventCallback = enableEthernetTethering(iface); + + // There is nothing more we can do on a physical interface without connecting an actual + // client, which is not possible in this test. + } + + private static final class MyTetheringEventCallback implements TetheringEventCallback { + private final TetheringManager mTm; + private final CountDownLatch mTetheringStartedLatch = new CountDownLatch(1); + private final CountDownLatch mTetheringStoppedLatch = new CountDownLatch(1); + private final CountDownLatch mClientConnectedLatch = new CountDownLatch(1); + private final String mIface; + + private volatile boolean mInterfaceWasTethered = false; + private volatile boolean mUnregistered = false; + private volatile Collection mClients = null; + + MyTetheringEventCallback(TetheringManager tm, String iface) { + mTm = tm; + mIface = iface; + } + + public void unregister() { + mTm.unregisterTetheringEventCallback(this); + mUnregistered = true; + } + + @Override + public void onTetheredInterfacesChanged(List interfaces) { + // Ignore stale callbacks registered by previous test cases. + if (mUnregistered) return; + + final boolean wasTethered = mTetheringStartedLatch.getCount() == 0; + if (!mInterfaceWasTethered && (mIface == null || interfaces.contains(mIface))) { + // This interface is being tethered for the first time. + Log.d(TAG, "Tethering started: " + interfaces); + mInterfaceWasTethered = true; + mTetheringStartedLatch.countDown(); + } else if (mInterfaceWasTethered && !interfaces.contains(mIface)) { + Log.d(TAG, "Tethering stopped: " + interfaces); + mTetheringStoppedLatch.countDown(); + } + } + + public void awaitInterfaceTethered() throws Exception { + assertTrue("Ethernet not tethered after " + TIMEOUT_MS + "ms", + mTetheringStartedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); + } + + public void awaitInterfaceUntethered() throws Exception { + // Don't block teardown if the interface was never tethered. + // This is racy because the interface might become tethered right after this check, but + // that can only happen in tearDown if startTethering timed out, which likely means + // the test has already failed. + if (!mInterfaceWasTethered) return; + + assertTrue(mIface + " not untethered after " + TIMEOUT_MS + "ms", + mTetheringStoppedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); + } + + @Override + public void onError(String ifName, int error) { + // Ignore stale callbacks registered by previous test cases. + if (mUnregistered) return; + + fail("TetheringEventCallback got error:" + error + " on iface " + ifName); + } + + @Override + public void onClientsChanged(Collection clients) { + // Ignore stale callbacks registered by previous test cases. + if (mUnregistered) return; + + Log.d(TAG, "Got clients changed: " + clients); + mClients = clients; + if (clients.size() > 0) { + mClientConnectedLatch.countDown(); + } + } + + public Collection awaitClientConnected() throws Exception { + assertTrue("Did not receive client connected callback after " + TIMEOUT_MS + "ms", + mClientConnectedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); + return mClients; + } + } + + private MyTetheringEventCallback enableEthernetTethering(String iface) throws Exception { + MyTetheringEventCallback callback = new MyTetheringEventCallback(mTm, iface); + mTm.registerTetheringEventCallback(mHandler::post, callback); + + StartTetheringCallback startTetheringCallback = new StartTetheringCallback() { + @Override + public void onTetheringFailed(int resultCode) { + fail("Unexpectedly got onTetheringFailed"); + } + }; + Log.d(TAG, "Starting Ethernet tethering"); + mTm.startTethering( + new TetheringRequest.Builder(TetheringManager.TETHERING_ETHERNET).build(), + mHandler::post /* executor */, startTetheringCallback); + callback.awaitInterfaceTethered(); + return callback; + } + + private int getMTU(TestNetworkInterface iface) throws SocketException { + NetworkInterface nif = NetworkInterface.getByName(iface.getInterfaceName()); + assertNotNull("Can't get NetworkInterface object for " + iface.getInterfaceName(), nif); + return nif.getMTU(); + } + + private void checkVirtualEthernet(TestNetworkInterface iface, int mtu) throws Exception { + FileDescriptor fd = iface.getFileDescriptor().getFileDescriptor(); + mTapPacketReader = new TapPacketReader(mHandler, fd, mtu); + mHandler.post(() -> mTapPacketReader.start()); + HandlerUtilsKt.waitForIdle(mHandler, TIMEOUT_MS); + + mTetheringEventCallback = enableEthernetTethering(iface.getInterfaceName()); + checkTetheredClientCallbacks(fd); + } + + private void checkTetheredClientCallbacks(FileDescriptor fd) throws Exception { + // Create a fake client. + byte[] clientMacAddr = new byte[6]; + new Random().nextBytes(clientMacAddr); + + // We have to retransmit DHCP requests because IpServer declares itself to be ready before + // its DhcpServer is actually started. TODO: fix this race and remove this loop. + DhcpPacket offerPacket = null; + for (int i = 0; i < DHCP_DISCOVER_ATTEMPTS; i++) { + Log.d(TAG, "Sending DHCP discover"); + sendDhcpDiscover(fd, clientMacAddr); + offerPacket = getNextDhcpPacket(); + if (offerPacket instanceof DhcpOfferPacket) break; + } + assertTrue("No DHCPOFFER received on interface within timeout", + offerPacket instanceof DhcpOfferPacket); + + sendDhcpRequest(fd, offerPacket, clientMacAddr); + DhcpPacket ackPacket = getNextDhcpPacket(); + assertTrue("No DHCPACK received on interface within timeout", + ackPacket instanceof DhcpAckPacket); + + final Collection clients = mTetheringEventCallback.awaitClientConnected(); + assertEquals(1, clients.size()); + final TetheredClient client = clients.iterator().next(); + + // Check the MAC address. + assertEquals(MacAddress.fromBytes(clientMacAddr), client.getMacAddress()); + assertEquals(TetheringManager.TETHERING_ETHERNET, client.getTetheringType()); + + // Check the hostname. + assertEquals(1, client.getAddresses().size()); + TetheredClient.AddressInfo info = client.getAddresses().get(0); + assertEquals(DHCP_HOSTNAME, info.getHostname()); + + // Check the address is the one that was handed out in the DHCP ACK. + DhcpResults dhcpResults = offerPacket.toDhcpResults(); + assertLinkAddressMatches(dhcpResults.ipAddress, info.getAddress()); + + // Check that the lifetime is correct +/- 10s. + final long now = SystemClock.elapsedRealtime(); + final long actualLeaseDuration = (info.getAddress().getExpirationTime() - now) / 1000; + final String msg = String.format("IP address should have lifetime of %d, got %d", + dhcpResults.leaseDuration, actualLeaseDuration); + assertTrue(msg, Math.abs(dhcpResults.leaseDuration - actualLeaseDuration) < 10); + } + + private DhcpPacket getNextDhcpPacket() throws ParseException { + byte[] packet; + while ((packet = mTapPacketReader.popPacket(PACKET_READ_TIMEOUT_MS)) != null) { + try { + return DhcpPacket.decodeFullPacket(packet, packet.length, DhcpPacket.ENCAP_L2); + } catch (DhcpPacket.ParseException e) { + // Not a DHCP packet. Continue. + } + } + return null; + } + + private static final class TetheredInterfaceRequester implements TetheredInterfaceCallback { + private final CountDownLatch mInterfaceAvailableLatch = new CountDownLatch(1); + private final Handler mHandler; + private final EthernetManager mEm; + + private volatile TetheredInterfaceRequest mRequest; + private volatile String mIface; + + TetheredInterfaceRequester(Handler handler, EthernetManager em) { + mHandler = handler; + mEm = em; + } + + @Override + public void onAvailable(String iface) { + Log.d(TAG, "Ethernet interface available: " + iface); + mIface = iface; + mInterfaceAvailableLatch.countDown(); + } + @Override + public void onUnavailable() {} + + public void requestInterface() { + assertNull("BUG: more than one tethered interface request", mRequest); + mRequest = mEm.requestTetheredInterface(mHandler::post, this); + } + + public String awaitRequestedInterface() throws InterruptedException { + assertTrue("No tethered interface available after " + TIMEOUT_MS + "ms", + mInterfaceAvailableLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); + return mIface; + } + + public void release() { + if (mRequest != null) { + mRequest.release(); + mRequest = null; + } + } + } + + private void sendDhcpDiscover(FileDescriptor fd, byte[] macAddress) throws Exception { + ByteBuffer packet = DhcpPacket.buildDiscoverPacket(DhcpPacket.ENCAP_L2, + new Random().nextInt() /* transactionId */, (short) 0 /* secs */, + macAddress, false /* unicast */, DHCP_REQUESTED_PARAMS, + false /* rapid commit */, DHCP_HOSTNAME); + sendPacket(fd, packet); + } + + private void sendDhcpRequest(FileDescriptor fd, DhcpPacket offerPacket, byte[] macAddress) + throws Exception { + DhcpResults results = offerPacket.toDhcpResults(); + Inet4Address clientIp = (Inet4Address) results.ipAddress.getAddress(); + Inet4Address serverIdentifier = results.serverAddress; + ByteBuffer packet = DhcpPacket.buildRequestPacket(DhcpPacket.ENCAP_L2, + 0 /* transactionId */, (short) 0 /* secs */, DhcpPacket.INADDR_ANY /* clientIp */, + false /* broadcast */, macAddress, clientIp /* requestedIpAddress */, + serverIdentifier, DHCP_REQUESTED_PARAMS, DHCP_HOSTNAME); + sendPacket(fd, packet); + } + + private void sendPacket(FileDescriptor fd, ByteBuffer packet) throws Exception { + assertNotNull("Only tests on virtual interfaces can send packets", fd); + Os.write(fd, packet); + } + + public void assertLinkAddressMatches(LinkAddress l1, LinkAddress l2) { + // Check all fields except the deprecation and expiry times. + String msg = String.format("LinkAddresses do not match. expected: %s actual: %s", l1, l2); + assertTrue(msg, l1.isSameAddressAs(l2)); + assertEquals("LinkAddress flags do not match", l1.getFlags(), l2.getFlags()); + assertEquals("LinkAddress scope does not match", l1.getScope(), l2.getScope()); + } + + private TestNetworkInterface createTestInterface() throws Exception { + TestNetworkManager tnm = mContext.getSystemService(TestNetworkManager.class); + TestNetworkInterface iface = tnm.createTapInterface(); + Log.d(TAG, "Created test interface " + iface.getInterfaceName()); + assertNotNull(NetworkInterface.getByName(iface.getInterfaceName())); + return iface; + } + + private void maybeDeleteTestInterface() throws Exception { + if (mTestIface != null) { + mTestIface.getFileDescriptor().close(); + Log.d(TAG, "Deleted test interface " + mTestIface.getInterfaceName()); + mTestIface = null; + } + } +} From 90ae90b6e0da8697d5b2c85021c2c7dd4f1c689f Mon Sep 17 00:00:00 2001 From: Anton Hansson Date: Fri, 27 Mar 2020 12:29:18 +0000 Subject: [PATCH 077/188] Feed framework-tethering-stubs the src filegroup This filegroups strips the "src" prefix away from the src path for the filter_packages check in droiddoc. Bug: 149293194 Test: m update-api (no change) Change-Id: I5b9ffa211be9c1a7dd8f63d5e7ba2a825d0d3190 --- Tethering/common/TetheringLib/Android.bp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Tethering/common/TetheringLib/Android.bp b/Tethering/common/TetheringLib/Android.bp index 6af5fe54f2..31c40d2a33 100644 --- a/Tethering/common/TetheringLib/Android.bp +++ b/Tethering/common/TetheringLib/Android.bp @@ -65,12 +65,7 @@ java_library { stubs_defaults { name: "framework-tethering-stubs-defaults", - srcs: [ - "src/android/net/TetheredClient.java", - "src/android/net/TetheringManager.java", - "src/android/net/TetheringConstants.java", - ], - libs: ["tethering-aidl-interfaces-java"], + srcs: [":framework-tethering-srcs"], } filegroup { From 6d4001132dac1edd2e006664b0bc599dc20e6c08 Mon Sep 17 00:00:00 2001 From: Mark Chien Date: Fri, 27 Mar 2020 09:37:01 +0000 Subject: [PATCH 078/188] Register callback and receiver after tethering is created Tethering service is created after boot complete which means most of the services are ready before tethering. Once tethering register the callback, callback event may come-in immediately. Make sure all of tethering related object is created, then register the callback, receiver, or listener. Bug: 149965121 Test: atest TetheringTests manual on/off tethering Change-Id: Ifdc427341db7d1313ad4b61207a96ab379d100aa Merged-In: I3941a186770679e7b476073d774e2310e25e44c6 (cherry picked from commit 285be1ee938ddc9728ccc3e951ed0ed1b2fa7117) --- .../connectivity/tethering/Tethering.java | 31 ++++++++++--------- .../tethering/TetheringService.java | 1 + .../connectivity/tethering/TetheringTest.java | 1 + 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java index c84892d675..4b2c9215f7 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java +++ b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java @@ -307,28 +307,22 @@ public class Tethering { userManager, this, mNotificationUpdater); mExecutor = new TetheringThreadExecutor(mHandler); mActiveDataSubIdListener = new ActiveDataSubIdListener(mExecutor); + mNetdCallback = new NetdCallback(); // Load tethering configuration. updateConfiguration(); - // NetdCallback should be registered after updateConfiguration() to ensure - // TetheringConfiguration is created. - mNetdCallback = new NetdCallback(); + } + + /** + * Start to register callbacks. + * Call this function when tethering is ready to handle callback events. + */ + public void startStateMachineUpdaters() { try { mNetd.registerUnsolicitedEventListener(mNetdCallback); } catch (RemoteException e) { mLog.e("Unable to register netd UnsolicitedEventListener"); } - - startStateMachineUpdaters(mHandler); - startTrackDefaultNetwork(); - - final WifiManager wifiManager = getWifiManager(); - if (wifiManager != null) { - wifiManager.registerSoftApCallback(mExecutor, new TetheringSoftApCallback()); - } - } - - private void startStateMachineUpdaters(Handler handler) { mCarrierConfigChange.startListening(); mContext.getSystemService(TelephonyManager.class).listen(mActiveDataSubIdListener, PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE); @@ -341,7 +335,14 @@ public class Tethering { filter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION); filter.addAction(UserManager.ACTION_USER_RESTRICTIONS_CHANGED); filter.addAction(ACTION_RESTRICT_BACKGROUND_CHANGED); - mContext.registerReceiver(mStateReceiver, filter, null, handler); + mContext.registerReceiver(mStateReceiver, filter, null, mHandler); + + final WifiManager wifiManager = getWifiManager(); + if (wifiManager != null) { + wifiManager.registerSoftApCallback(mExecutor, new TetheringSoftApCallback()); + } + + startTrackDefaultNetwork(); } private class TetheringThreadExecutor implements Executor { diff --git a/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java b/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java index c5329d8d33..c30be25dbd 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java +++ b/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java @@ -80,6 +80,7 @@ public class TetheringService extends Service { mContext = mDeps.getContext(); mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); mTethering = makeTethering(mDeps); + mTethering.startStateMachineUpdaters(); } /** diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java index 5ead1106d7..a59c6fd9e1 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java @@ -484,6 +484,7 @@ public class TetheringTest { mServiceContext.registerReceiver(mBroadcastReceiver, new IntentFilter(ACTION_TETHER_STATE_CHANGED)); mTethering = makeTethering(); + mTethering.startStateMachineUpdaters(); verify(mStatsManager, times(1)).registerNetworkStatsProvider(anyString(), any()); verify(mNetd).registerUnsolicitedEventListener(any()); final ArgumentCaptor phoneListenerCaptor = From f1460fad5252dfea1031c463a40e4202cf1d24e9 Mon Sep 17 00:00:00 2001 From: Jooyung Han Date: Mon, 30 Mar 2020 13:23:01 +0900 Subject: [PATCH 079/188] Add min_sdk_version:R to updatable apexes APEXes introduced in R need to set min_sdk_version to ensure that they are built against correct version(30 or R) of stubs (libc/liblog/...). Bug: 152655956 Test: /vendor/google/build/build_mainline_modules.sh Change-Id: I4a893c34b09334eea124266287301e479b9e8a59 --- Tethering/apex/Android.bp | 1 + 1 file changed, 1 insertion(+) diff --git a/Tethering/apex/Android.bp b/Tethering/apex/Android.bp index 96a4d20077..24df5f6960 100644 --- a/Tethering/apex/Android.bp +++ b/Tethering/apex/Android.bp @@ -17,6 +17,7 @@ apex { name: "com.android.tethering", updatable: true, + min_sdk_version: "R", java_libs: ["framework-tethering"], apps: ["Tethering"], manifest: "manifest.json", From e6107d24669713b38c1552b6917f2a84b8eaacc8 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 6 Apr 2020 09:19:57 +0000 Subject: [PATCH 080/188] Migrate to TetherOffloadRuleParcel in IpServer The netd tethering offload IPCs are changing from taking a list of primitives to taking a TetherOffloadRuleParcel. Modify their only caller. Bug: 140541991 Test: atest IpServerTest Merged-In: I83718c80ef9d31199c87021b4dd5821717fd5ba5 Change-Id: I83718c80ef9d31199c87021b4dd5821717fd5ba5 --- Tethering/src/android/net/ip/IpServer.java | 20 +++- .../unit/src/android/net/ip/IpServerTest.java | 109 +++++++++++++----- 2 files changed, 95 insertions(+), 34 deletions(-) diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java index c5478d2e1a..cf57e2fc2c 100644 --- a/Tethering/src/android/net/ip/IpServer.java +++ b/Tethering/src/android/net/ip/IpServer.java @@ -33,6 +33,7 @@ import android.net.LinkAddress; import android.net.LinkProperties; import android.net.MacAddress; import android.net.RouteInfo; +import android.net.TetherOffloadRuleParcel; import android.net.TetheredClient; import android.net.TetheringManager; import android.net.TetheringRequestParcel; @@ -279,6 +280,19 @@ public class IpServer extends StateMachine { return new Ipv6ForwardingRule(newUpstreamIfindex, downstreamIfindex, address, srcMac, dstMac); } + + // Don't manipulate TetherOffloadRuleParcel directly because implementing onNewUpstream() + // would be error-prone due to generated stable AIDL classes not having a copy constructor. + public TetherOffloadRuleParcel toTetherOffloadRuleParcel() { + final TetherOffloadRuleParcel parcel = new TetherOffloadRuleParcel(); + parcel.inputInterfaceIndex = upstreamIfindex; + parcel.outputInterfaceIndex = downstreamIfindex; + parcel.destination = address.getAddress(); + parcel.prefixLength = 128; + parcel.srcL2Address = srcMac.toByteArray(); + parcel.dstL2Address = dstMac.toByteArray(); + return parcel; + } } private final LinkedHashMap mIpv6ForwardingRules = new LinkedHashMap<>(); @@ -815,9 +829,7 @@ public class IpServer extends StateMachine { private void addIpv6ForwardingRule(Ipv6ForwardingRule rule) { try { - mNetd.tetherRuleAddDownstreamIpv6(mInterfaceParams.index, rule.upstreamIfindex, - rule.address.getAddress(), mInterfaceParams.macAddr.toByteArray(), - rule.dstMac.toByteArray()); + mNetd.tetherOffloadRuleAdd(rule.toTetherOffloadRuleParcel()); mIpv6ForwardingRules.put(rule.address, rule); } catch (RemoteException | ServiceSpecificException e) { mLog.e("Could not add IPv6 downstream rule: ", e); @@ -826,7 +838,7 @@ public class IpServer extends StateMachine { private void removeIpv6ForwardingRule(Ipv6ForwardingRule rule, boolean removeFromMap) { try { - mNetd.tetherRuleRemoveDownstreamIpv6(rule.upstreamIfindex, rule.address.getAddress()); + mNetd.tetherOffloadRuleRemove(rule.toTetherOffloadRuleParcel()); if (removeFromMap) { mIpv6ForwardingRules.remove(rule.address); } diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java index 3106e0e5e1..fdfdae837d 100644 --- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java +++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java @@ -43,7 +43,6 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doAnswer; @@ -66,6 +65,7 @@ import android.net.LinkAddress; import android.net.LinkProperties; import android.net.MacAddress; import android.net.RouteInfo; +import android.net.TetherOffloadRuleParcel; import android.net.dhcp.DhcpServingParamsParcel; import android.net.dhcp.IDhcpServer; import android.net.dhcp.IDhcpServerCallbacks; @@ -85,6 +85,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatcher; import org.mockito.Captor; import org.mockito.InOrder; import org.mockito.Mock; @@ -92,6 +93,7 @@ import org.mockito.MockitoAnnotations; import java.net.Inet4Address; import java.net.InetAddress; +import java.util.Arrays; @RunWith(AndroidJUnit4.class) @SmallTest @@ -514,6 +516,65 @@ public class IpServerTest { mLooper.dispatchAll(); } + /** + * Custom ArgumentMatcher for TetherOffloadRuleParcel. This is needed because generated stable + * AIDL classes don't have equals(), so we cannot just use eq(). A custom assert, such as: + * + * private void checkFooCalled(StableParcelable p, ...) { + * ArgumentCaptor captor = ArgumentCaptor.forClass(FooParam.class); + * verify(mMock).foo(captor.capture()); + * Foo foo = captor.getValue(); + * assertFooMatchesExpectations(foo); + * } + * + * almost works, but not quite. This is because if the code under test calls foo() twice, the + * first call to checkFooCalled() matches both the calls, putting both calls into the captor, + * and then fails with TooManyActualInvocations. It also makes it harder to use other mockito + * features such as never(), inOrder(), etc. + * + * This approach isn't great because if the match fails, the error message is unhelpful + * (actual: "android.net.TetherOffloadRuleParcel@8c827b0" or some such), but at least it does + * work. + * + * See ConnectivityServiceTest#assertRoutesAdded for an alternative approach which solves the + * TooManyActualInvocations problem described above by forcing the caller of the custom assert + * method to specify all expected invocations in one call. This is useful when the stable + * parcelable class being asserted on has a corresponding Java object (eg., RouteInfo and + * RouteInfoParcelable), and the caller can just pass in a list of them. It not useful here + * because there is no such object. + */ + private static class TetherOffloadRuleParcelMatcher implements + ArgumentMatcher { + public final int upstreamIfindex; + public final InetAddress dst; + public final MacAddress dstMac; + + TetherOffloadRuleParcelMatcher(int upstreamIfindex, InetAddress dst, MacAddress dstMac) { + this.upstreamIfindex = upstreamIfindex; + this.dst = dst; + this.dstMac = dstMac; + } + + public boolean matches(TetherOffloadRuleParcel parcel) { + return upstreamIfindex == parcel.inputInterfaceIndex + && (TEST_IFACE_PARAMS.index == parcel.outputInterfaceIndex) + && Arrays.equals(dst.getAddress(), parcel.destination) + && (128 == parcel.prefixLength) + && Arrays.equals(TEST_IFACE_PARAMS.macAddr.toByteArray(), parcel.srcL2Address) + && Arrays.equals(dstMac.toByteArray(), parcel.dstL2Address); + } + + public String toString() { + return String.format("TetherOffloadRuleParcelMatcher(%d, %s, %s", + upstreamIfindex, dst.getHostAddress(), dstMac); + } + } + + private TetherOffloadRuleParcel matches( + int upstreamIfindex, InetAddress dst, MacAddress dstMac) { + return argThat(new TetherOffloadRuleParcelMatcher(upstreamIfindex, dst, dstMac)); + } + @Test public void addRemoveipv6ForwardingRules() throws Exception { initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */); @@ -537,13 +598,11 @@ public class IpServerTest { // Events on this interface are received and sent to netd. recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA); - verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX), - eq(neighA.getAddress()), eq(myMac.toByteArray()), eq(macA.toByteArray())); + verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA)); reset(mNetd); recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB); - verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX), - eq(neighB.getAddress()), eq(myMac.toByteArray()), eq(macB.toByteArray())); + verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighB, macB)); reset(mNetd); // Link-local and multicast neighbors are ignored. @@ -554,12 +613,12 @@ public class IpServerTest { // A neighbor that is no longer valid causes the rule to be removed. recvNewNeigh(myIfindex, neighA, NUD_FAILED, macA); - verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighA.getAddress())); + verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macA)); reset(mNetd); // A neighbor that is deleted causes the rule to be removed. recvDelNeigh(myIfindex, neighB, NUD_STALE, macB); - verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighB.getAddress())); + verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB)); reset(mNetd); // Upstream changes result in deleting and re-adding the rules. @@ -571,22 +630,16 @@ public class IpServerTest { LinkProperties lp = new LinkProperties(); lp.setInterfaceName(UPSTREAM_IFACE2); dispatchTetherConnectionChanged(UPSTREAM_IFACE2, lp); - inOrder.verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX2), - eq(neighA.getAddress()), eq(myMac.toByteArray()), eq(macA.toByteArray())); - inOrder.verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), - eq(neighA.getAddress())); - inOrder.verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX2), - eq(neighB.getAddress()), eq(myMac.toByteArray()), eq(macB.toByteArray())); - inOrder.verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), - eq(neighB.getAddress())); + inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX2, neighA, macA)); + inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macA)); + inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX2, neighB, macB)); + inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB)); reset(mNetd); // When the upstream is lost, rules are removed. dispatchTetherConnectionChanged(null, null); - verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX2), - eq(neighA.getAddress())); - verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX2), - eq(neighB.getAddress())); + verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX2, neighA, macA)); + verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX2, neighB, macB)); reset(mNetd); // If the upstream is IPv4-only, no rules are added. @@ -599,31 +652,27 @@ public class IpServerTest { lp.setInterfaceName(UPSTREAM_IFACE); dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp); recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB); - verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX), - eq(neighB.getAddress()), eq(myMac.toByteArray()), eq(macB.toByteArray())); - verify(mNetd, never()).tetherRuleAddDownstreamIpv6(anyInt(), anyInt(), - eq(neighA.getAddress()), any(), any()); + verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighB, macB)); + verify(mNetd, never()).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA)); // If upstream IPv6 connectivity is lost, rules are removed. reset(mNetd); dispatchTetherConnectionChanged(UPSTREAM_IFACE, null); - verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighB.getAddress())); + verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB)); // When the interface goes down, rules are removed. lp.setInterfaceName(UPSTREAM_IFACE); dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp); recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA); recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB); - verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX), - eq(neighA.getAddress()), eq(myMac.toByteArray()), eq(macA.toByteArray())); - verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX), - eq(neighB.getAddress()), eq(myMac.toByteArray()), eq(macB.toByteArray())); + verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA)); + verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighB, macB)); reset(mNetd); mIpServer.stop(); mLooper.dispatchAll(); - verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighA.getAddress())); - verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighB.getAddress())); + verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macA)); + verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB)); reset(mNetd); } From bd8d7a820831083bad22bec3b414a83ead50f7df Mon Sep 17 00:00:00 2001 From: Paul Hu Date: Mon, 6 Apr 2020 08:52:54 +0000 Subject: [PATCH 081/188] Fix EntitlementManager issues 1. Add TETHERING_ETHERNET to vaild downstream type. So starting ethernet tethering will do entitlement check as well. 2. Ignore request with invalid downstream type on handleRequestLatestTetheringEntitlementValue() Bug: 152828758 Bug: 152828142 Test: atests TetheringTests CtsTetheringTest Change-Id: Id0cb59cc4681f5ffbde7be54de05a05e46f0ffb8 Merged-In: Id0cb59cc4681f5ffbde7be54de05a05e46f0ffb8 (cherry picked from commit c502e050fd9543e8bde45014dd66ea1be91c90ef) --- .../tethering/EntitlementManager.java | 7 ++++ .../tethering/EntitlementManagerTest.java | 32 +++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java b/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java index bd60594f27..639cf65d79 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java +++ b/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java @@ -20,6 +20,7 @@ import static android.net.TetheringConstants.EXTRA_ADD_TETHER_TYPE; import static android.net.TetheringConstants.EXTRA_PROVISION_CALLBACK; import static android.net.TetheringConstants.EXTRA_RUN_PROVISION; import static android.net.TetheringManager.TETHERING_BLUETOOTH; +import static android.net.TetheringManager.TETHERING_ETHERNET; import static android.net.TetheringManager.TETHERING_INVALID; import static android.net.TetheringManager.TETHERING_USB; import static android.net.TetheringManager.TETHERING_WIFI; @@ -537,6 +538,7 @@ public class EntitlementManager { private static boolean isValidDownstreamType(int type) { switch (type) { case TETHERING_BLUETOOTH: + case TETHERING_ETHERNET: case TETHERING_USB: case TETHERING_WIFI: return true; @@ -650,6 +652,11 @@ public class EntitlementManager { private void handleRequestLatestTetheringEntitlementValue(int downstream, ResultReceiver receiver, boolean showEntitlementUi) { + if (!isValidDownstreamType(downstream)) { + receiver.send(TETHER_ERROR_ENTITLEMENT_UNKNOWN, null); + return; + } + final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); if (!isTetherProvisioningRequired(config)) { receiver.send(TETHER_ERROR_NO_ERROR, null); diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java index 0a7850b680..b3a30abca6 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java +++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java @@ -17,8 +17,10 @@ package com.android.server.connectivity.tethering; import static android.net.TetheringManager.TETHERING_BLUETOOTH; +import static android.net.TetheringManager.TETHERING_ETHERNET; import static android.net.TetheringManager.TETHERING_USB; import static android.net.TetheringManager.TETHERING_WIFI; +import static android.net.TetheringManager.TETHERING_WIFI_P2P; import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN; import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; import static android.net.TetheringManager.TETHER_ERROR_PROVISIONING_FAILED; @@ -353,6 +355,20 @@ public final class EntitlementManagerTest { callbackTimeoutHelper(mCallbacklatch); assertEquals(0, mEnMgr.uiProvisionCount); mEnMgr.reset(); + // 8. Test get value for invalid downstream type. + mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; + receiver = new ResultReceiver(null) { + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + assertEquals(TETHER_ERROR_ENTITLEMENT_UNKNOWN, resultCode); + mCallbacklatch.countDown(); + } + }; + mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI_P2P, receiver, true); + mLooper.dispatchAll(); + callbackTimeoutHelper(mCallbacklatch); + assertEquals(0, mEnMgr.uiProvisionCount); + mEnMgr.reset(); } void callbackTimeoutHelper(final CountDownLatch latch) throws Exception { @@ -471,6 +487,22 @@ public final class EntitlementManagerTest { mLooper.dispatchAll(); assertEquals(0, mEnMgr.uiProvisionCount); assertEquals(3, mEnMgr.silentProvisionCount); + assertFalse(mEnMgr.isCellularUpstreamPermitted()); + mEnMgr.reset(); + // 7. start ui provisioning, upstream is mobile, downstream is ethernet + mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; + mEnMgr.startProvisioningIfNeeded(TETHERING_ETHERNET, true); + mLooper.dispatchAll(); + assertEquals(1, mEnMgr.uiProvisionCount); + assertEquals(0, mEnMgr.silentProvisionCount); + assertTrue(mEnMgr.isCellularUpstreamPermitted()); + mEnMgr.reset(); + // 8. downstream is invalid + mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; + mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI_P2P, true); + mLooper.dispatchAll(); + assertEquals(0, mEnMgr.uiProvisionCount); + assertEquals(0, mEnMgr.silentProvisionCount); mEnMgr.reset(); } From 06c8ba0cd79bd687487c61c06e27244bada1822a Mon Sep 17 00:00:00 2001 From: Xiao Ma Date: Mon, 6 Apr 2020 11:24:48 +0000 Subject: [PATCH 082/188] Rename IDhcpLeaseCallbacks to IDhcpEventCallbacks for more generic. Bug: 130741856 Test: atest TetheringTests Merged-In: I66614fbf67fba1e7dab0b8a2d41bc30a726e4f38 Change-Id: I66614fbf67fba1e7dab0b8a2d41bc30a726e4f38 --- Tethering/src/android/net/ip/IpServer.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java index c5478d2e1a..780da77515 100644 --- a/Tethering/src/android/net/ip/IpServer.java +++ b/Tethering/src/android/net/ip/IpServer.java @@ -40,7 +40,7 @@ import android.net.dhcp.DhcpLeaseParcelable; import android.net.dhcp.DhcpServerCallbacks; import android.net.dhcp.DhcpServingParamsParcel; import android.net.dhcp.DhcpServingParamsParcelExt; -import android.net.dhcp.IDhcpLeaseCallbacks; +import android.net.dhcp.IDhcpEventCallbacks; import android.net.dhcp.IDhcpServer; import android.net.ip.IpNeighborMonitor.NeighborEvent; import android.net.ip.RouterAdvertisementDaemon.RaParams; @@ -448,7 +448,7 @@ public class IpServer extends StateMachine { } } - private class DhcpLeaseCallback extends IDhcpLeaseCallbacks.Stub { + private class DhcpLeaseCallback extends IDhcpEventCallbacks.Stub { @Override public void onLeasesChanged(List leaseParcelables) { final ArrayList leases = new ArrayList<>(); @@ -481,6 +481,11 @@ public class IpServer extends StateMachine { }); } + @Override + public void onNewPrefixRequest(IpPrefix currentPrefix) { + //TODO: add specific implementation. + } + @Override public int getInterfaceVersion() { return this.VERSION; From 46b44053da510522f3887685237f817dac8d4943 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 30 Mar 2020 03:38:51 +0000 Subject: [PATCH 083/188] Use CompletableFuture instead of a latch. Addresses review comments on aosp/1260100. Bug: 150644681 Test: test-only change Merged-In: Ia73ba8a121a3744a5e36795d2d2bff2f099c1394 Change-Id: Ia73ba8a121a3744a5e36795d2d2bff2f099c1394 --- .../android/net/EthernetTetheringTest.java | 43 +++++++++---------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java index 843a4f19c3..492ce3db34 100644 --- a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java +++ b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java @@ -63,6 +63,7 @@ import java.nio.ByteBuffer; import java.util.Collection; import java.util.List; import java.util.Random; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -150,10 +151,7 @@ public class EthernetTetheringTest { Log.d(TAG, "Including test interfaces"); mEm.setIncludeTestInterfaces(true); - Log.d(TAG, "Requesting tethered interface"); - mTetheredInterfaceRequester.requestInterface(); - - final String iface = mTetheredInterfaceRequester.awaitRequestedInterface(); + final String iface = mTetheredInterfaceRequester.getInterface(); assertEquals("TetheredInterfaceCallback for unexpected interface", mTestIface.getInterfaceName(), iface); @@ -165,14 +163,13 @@ public class EthernetTetheringTest { // This test requires manipulating packets. Skip if there is a physical Ethernet connected. assumeFalse(mEm.isAvailable()); - Log.d(TAG, "Requesting tethered interface"); - mTetheredInterfaceRequester.requestInterface(); + CompletableFuture futureIface = mTetheredInterfaceRequester.requestInterface(); mEm.setIncludeTestInterfaces(true); mTestIface = createTestInterface(); - final String iface = mTetheredInterfaceRequester.awaitRequestedInterface(); + final String iface = futureIface.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); assertEquals("TetheredInterfaceCallback for unexpected interface", mTestIface.getInterfaceName(), iface); @@ -184,8 +181,7 @@ public class EthernetTetheringTest { assumeTrue(mEm.isAvailable()); // Get an interface to use. - mTetheredInterfaceRequester.requestInterface(); - String iface = mTetheredInterfaceRequester.awaitRequestedInterface(); + final String iface = mTetheredInterfaceRequester.getInterface(); // Enable Ethernet tethering and check that it starts. mTetheringEventCallback = enableEthernetTethering(iface); @@ -373,8 +369,8 @@ public class EthernetTetheringTest { private final Handler mHandler; private final EthernetManager mEm; - private volatile TetheredInterfaceRequest mRequest; - private volatile String mIface; + private TetheredInterfaceRequest mRequest; + private final CompletableFuture mFuture = new CompletableFuture<>(); TetheredInterfaceRequester(Handler handler, EthernetManager em) { mHandler = handler; @@ -384,25 +380,28 @@ public class EthernetTetheringTest { @Override public void onAvailable(String iface) { Log.d(TAG, "Ethernet interface available: " + iface); - mIface = iface; - mInterfaceAvailableLatch.countDown(); + mFuture.complete(iface); } + @Override - public void onUnavailable() {} - - public void requestInterface() { - assertNull("BUG: more than one tethered interface request", mRequest); - mRequest = mEm.requestTetheredInterface(mHandler::post, this); + public void onUnavailable() { + mFuture.completeExceptionally(new IllegalStateException("onUnavailable received")); } - public String awaitRequestedInterface() throws InterruptedException { - assertTrue("No tethered interface available after " + TIMEOUT_MS + "ms", - mInterfaceAvailableLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); - return mIface; + public CompletableFuture requestInterface() { + assertNull("BUG: more than one tethered interface request", mRequest); + Log.d(TAG, "Requesting tethered interface"); + mRequest = mEm.requestTetheredInterface(mHandler::post, this); + return mFuture; + } + + public String getInterface() throws Exception { + return requestInterface().get(TIMEOUT_MS, TimeUnit.MILLISECONDS); } public void release() { if (mRequest != null) { + mFuture.obtrudeException(new IllegalStateException("Request already released")); mRequest.release(); mRequest = null; } From 7f6ab40294d26e339c0da997d0154e00ecf60598 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Fri, 3 Apr 2020 08:41:51 +0000 Subject: [PATCH 084/188] Add a test for static IPv4 address tethering configuration. Bug: 150644681 Test: test-only change Merged-In: I8f4a99da2351fdb5467f561a9732b14a8ebf674b Change-Id: I8f4a99da2351fdb5467f561a9732b14a8ebf674b --- .../android/net/EthernetTetheringTest.java | 129 +++++++++++++++--- 1 file changed, 109 insertions(+), 20 deletions(-) diff --git a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java index 492ce3db34..dbd68ef77c 100644 --- a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java +++ b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java @@ -18,6 +18,7 @@ package android.net; import static android.Manifest.permission.MANAGE_TEST_NETWORKS; import static android.Manifest.permission.NETWORK_SETTINGS; +import static android.net.TetheringManager.TETHERING_ETHERNET; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -57,6 +58,7 @@ import org.junit.runner.RunWith; import java.io.FileDescriptor; import java.net.Inet4Address; +import java.net.InterfaceAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.nio.ByteBuffer; @@ -66,6 +68,7 @@ import java.util.Random; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; @RunWith(AndroidJUnit4.class) @MediumTest @@ -110,7 +113,7 @@ public class EthernetTetheringTest { } private void cleanUp() throws Exception { - mTm.stopTethering(TetheringManager.TETHERING_ETHERNET); + mTm.stopTethering(TETHERING_ETHERNET); if (mTetheringEventCallback != null) { mTetheringEventCallback.awaitInterfaceUntethered(); mTetheringEventCallback.unregister(); @@ -176,6 +179,49 @@ public class EthernetTetheringTest { checkVirtualEthernet(mTestIface, getMTU(mTestIface)); } + @Test + public void testStaticIpv4() throws Exception { + assumeFalse(mEm.isAvailable()); + + mEm.setIncludeTestInterfaces(true); + + mTestIface = createTestInterface(); + + final String iface = mTetheredInterfaceRequester.getInterface(); + assertEquals("TetheredInterfaceCallback for unexpected interface", + mTestIface.getInterfaceName(), iface); + + assertInvalidStaticIpv4Request(iface, null, null); + assertInvalidStaticIpv4Request(iface, "2001:db8::1/64", "2001:db8:2::/64"); + assertInvalidStaticIpv4Request(iface, "192.0.2.2/28", "2001:db8:2::/28"); + assertInvalidStaticIpv4Request(iface, "2001:db8:2::/28", "192.0.2.2/28"); + assertInvalidStaticIpv4Request(iface, "192.0.2.2/28", null); + assertInvalidStaticIpv4Request(iface, null, "192.0.2.2/28"); + assertInvalidStaticIpv4Request(iface, "192.0.2.3/27", "192.0.2.2/28"); + + final String localAddr = "192.0.2.3/28"; + final String clientAddr = "192.0.2.2/28"; + mTetheringEventCallback = enableEthernetTethering(iface, + requestWithStaticIpv4(localAddr, clientAddr)); + + mTetheringEventCallback.awaitInterfaceTethered(); + assertInterfaceHasIpAddress(iface, clientAddr); + + byte[] client1 = MacAddress.fromString("1:2:3:4:5:6").toByteArray(); + byte[] client2 = MacAddress.fromString("a:b:c:d:e:f").toByteArray(); + + FileDescriptor fd = mTestIface.getFileDescriptor().getFileDescriptor(); + mTapPacketReader = makePacketReader(fd, getMTU(mTestIface)); + DhcpResults dhcpResults = runDhcp(fd, client1); + assertEquals(new LinkAddress(clientAddr), dhcpResults.ipAddress); + + try { + runDhcp(fd, client2); + fail("Only one client should get an IP address"); + } catch (TimeoutException expected) { } + + } + @Test public void testPhysicalEthernet() throws Exception { assumeTrue(mEm.isAvailable()); @@ -271,7 +317,8 @@ public class EthernetTetheringTest { } } - private MyTetheringEventCallback enableEthernetTethering(String iface) throws Exception { + private MyTetheringEventCallback enableEthernetTethering(String iface, + TetheringRequest request) throws Exception { MyTetheringEventCallback callback = new MyTetheringEventCallback(mTm, iface); mTm.registerTetheringEventCallback(mHandler::post, callback); @@ -282,34 +329,37 @@ public class EthernetTetheringTest { } }; Log.d(TAG, "Starting Ethernet tethering"); - mTm.startTethering( - new TetheringRequest.Builder(TetheringManager.TETHERING_ETHERNET).build(), - mHandler::post /* executor */, startTetheringCallback); + mTm.startTethering(request, mHandler::post /* executor */, startTetheringCallback); callback.awaitInterfaceTethered(); return callback; } + private MyTetheringEventCallback enableEthernetTethering(String iface) throws Exception { + return enableEthernetTethering(iface, + new TetheringRequest.Builder(TETHERING_ETHERNET).build()); + } + private int getMTU(TestNetworkInterface iface) throws SocketException { NetworkInterface nif = NetworkInterface.getByName(iface.getInterfaceName()); assertNotNull("Can't get NetworkInterface object for " + iface.getInterfaceName(), nif); return nif.getMTU(); } + private TapPacketReader makePacketReader(FileDescriptor fd, int mtu) { + final TapPacketReader reader = new TapPacketReader(mHandler, fd, mtu); + mHandler.post(() -> reader.start()); + HandlerUtilsKt.waitForIdle(mHandler, TIMEOUT_MS); + return reader; + } + private void checkVirtualEthernet(TestNetworkInterface iface, int mtu) throws Exception { FileDescriptor fd = iface.getFileDescriptor().getFileDescriptor(); - mTapPacketReader = new TapPacketReader(mHandler, fd, mtu); - mHandler.post(() -> mTapPacketReader.start()); - HandlerUtilsKt.waitForIdle(mHandler, TIMEOUT_MS); - + mTapPacketReader = makePacketReader(fd, mtu); mTetheringEventCallback = enableEthernetTethering(iface.getInterfaceName()); checkTetheredClientCallbacks(fd); } - private void checkTetheredClientCallbacks(FileDescriptor fd) throws Exception { - // Create a fake client. - byte[] clientMacAddr = new byte[6]; - new Random().nextBytes(clientMacAddr); - + private DhcpResults runDhcp(FileDescriptor fd, byte[] clientMacAddr) throws Exception { // We have to retransmit DHCP requests because IpServer declares itself to be ready before // its DhcpServer is actually started. TODO: fix this race and remove this loop. DhcpPacket offerPacket = null; @@ -319,13 +369,25 @@ public class EthernetTetheringTest { offerPacket = getNextDhcpPacket(); if (offerPacket instanceof DhcpOfferPacket) break; } - assertTrue("No DHCPOFFER received on interface within timeout", - offerPacket instanceof DhcpOfferPacket); + if (!(offerPacket instanceof DhcpOfferPacket)) { + throw new TimeoutException("No DHCPOFFER received on interface within timeout"); + } sendDhcpRequest(fd, offerPacket, clientMacAddr); DhcpPacket ackPacket = getNextDhcpPacket(); - assertTrue("No DHCPACK received on interface within timeout", - ackPacket instanceof DhcpAckPacket); + if (!(ackPacket instanceof DhcpAckPacket)) { + throw new TimeoutException("No DHCPACK received on interface within timeout"); + } + + return ackPacket.toDhcpResults(); + } + + private void checkTetheredClientCallbacks(FileDescriptor fd) throws Exception { + // Create a fake client. + byte[] clientMacAddr = new byte[6]; + new Random().nextBytes(clientMacAddr); + + DhcpResults dhcpResults = runDhcp(fd, clientMacAddr); final Collection clients = mTetheringEventCallback.awaitClientConnected(); assertEquals(1, clients.size()); @@ -333,7 +395,7 @@ public class EthernetTetheringTest { // Check the MAC address. assertEquals(MacAddress.fromBytes(clientMacAddr), client.getMacAddress()); - assertEquals(TetheringManager.TETHERING_ETHERNET, client.getTetheringType()); + assertEquals(TETHERING_ETHERNET, client.getTetheringType()); // Check the hostname. assertEquals(1, client.getAddresses().size()); @@ -341,7 +403,6 @@ public class EthernetTetheringTest { assertEquals(DHCP_HOSTNAME, info.getHostname()); // Check the address is the one that was handed out in the DHCP ACK. - DhcpResults dhcpResults = offerPacket.toDhcpResults(); assertLinkAddressMatches(dhcpResults.ipAddress, info.getAddress()); // Check that the lifetime is correct +/- 10s. @@ -441,6 +502,34 @@ public class EthernetTetheringTest { assertEquals("LinkAddress scope does not match", l1.getScope(), l2.getScope()); } + private TetheringRequest requestWithStaticIpv4(String local, String client) { + LinkAddress localAddr = local == null ? null : new LinkAddress(local); + LinkAddress clientAddr = client == null ? null : new LinkAddress(client); + return new TetheringRequest.Builder(TETHERING_ETHERNET) + .setStaticIpv4Addresses(localAddr, clientAddr).build(); + } + + private void assertInvalidStaticIpv4Request(String iface, String local, String client) + throws Exception { + try { + enableEthernetTethering(iface, requestWithStaticIpv4(local, client)); + fail("Unexpectedly accepted invalid IPv4 configuration: " + local + ", " + client); + } catch (IllegalArgumentException | NullPointerException expected) { } + } + + private void assertInterfaceHasIpAddress(String iface, String expected) throws Exception { + LinkAddress expectedAddr = new LinkAddress(expected); + NetworkInterface nif = NetworkInterface.getByName(iface); + for (InterfaceAddress ia : nif.getInterfaceAddresses()) { + final LinkAddress addr = new LinkAddress(ia.getAddress(), ia.getNetworkPrefixLength()); + if (expectedAddr.equals(addr)) { + return; + } + } + fail("Expected " + iface + " to have IP address " + expected + ", found " + + nif.getInterfaceAddresses()); + } + private TestNetworkInterface createTestInterface() throws Exception { TestNetworkManager tnm = mContext.getSystemService(TestNetworkManager.class); TestNetworkInterface iface = tnm.createTapInterface(); From 5e9e0337c5aab1b2d9176d541d5d084aba989bbe Mon Sep 17 00:00:00 2001 From: Mark Chien Date: Mon, 6 Apr 2020 16:59:51 +0000 Subject: [PATCH 085/188] Add IPv6TetheringCoordinator unit test Bug: 148636687 Test: atest IPv6TetheringCoordinatorTest Change-Id: I1ba30bb524cbc0cc2782ce1702f9889c0d8a7576 Merged-In: I939323ce09adb9c66b1e2b83d58b0f892aa8f011 (cherry picked from commit 368c56dd7133d292c5f3ab5942016bb4dd1fbc88) --- .../IPv6TetheringCoordinatorTest.java | 156 ++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 Tethering/tests/unit/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinatorTest.java diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinatorTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinatorTest.java new file mode 100644 index 0000000000..912124357c --- /dev/null +++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinatorTest.java @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.connectivity.tethering; + +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; +import static android.net.RouteInfo.RTN_UNICAST; +import static android.net.ip.IpServer.STATE_LOCAL_ONLY; +import static android.net.ip.IpServer.STATE_TETHERED; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import android.net.InetAddresses; +import android.net.IpPrefix; +import android.net.LinkAddress; +import android.net.LinkProperties; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.RouteInfo; +import android.net.ip.IpServer; +import android.net.util.SharedLog; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.List; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class IPv6TetheringCoordinatorTest { + private static final String TEST_DNS_SERVER = "2001:4860:4860::8888"; + private static final String TEST_INTERFACE = "test_rmnet0"; + private static final String TEST_IPV6_ADDRESS = "2001:db8::1/64"; + private static final String TEST_IPV4_ADDRESS = "192.168.100.1/24"; + + private IPv6TetheringCoordinator mIPv6TetheringCoordinator; + private ArrayList mNotifyList; + + @Mock private SharedLog mSharedLog; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + when(mSharedLog.forSubComponent(anyString())).thenReturn(mSharedLog); + mNotifyList = new ArrayList(); + mIPv6TetheringCoordinator = new IPv6TetheringCoordinator(mNotifyList, mSharedLog); + } + + private UpstreamNetworkState createDualStackUpstream(final int transportType) { + final Network network = mock(Network.class); + final NetworkCapabilities netCap = + new NetworkCapabilities.Builder().addTransportType(transportType).build(); + final InetAddress dns = InetAddresses.parseNumericAddress(TEST_DNS_SERVER); + final LinkProperties linkProp = new LinkProperties(); + linkProp.setInterfaceName(TEST_INTERFACE); + linkProp.addLinkAddress(new LinkAddress(TEST_IPV6_ADDRESS)); + linkProp.addLinkAddress(new LinkAddress(TEST_IPV4_ADDRESS)); + linkProp.addRoute(new RouteInfo(new IpPrefix("::/0"), null, TEST_INTERFACE, RTN_UNICAST)); + linkProp.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0"), null, TEST_INTERFACE, + RTN_UNICAST)); + linkProp.addDnsServer(dns); + return new UpstreamNetworkState(linkProp, netCap, network); + } + + private void assertOnlyOneV6AddressAndNoV4(LinkProperties lp) { + assertEquals(lp.getInterfaceName(), TEST_INTERFACE); + assertFalse(lp.hasIpv4Address()); + final List addresses = lp.getLinkAddresses(); + assertEquals(addresses.size(), 1); + final LinkAddress v6Address = addresses.get(0); + assertEquals(v6Address, new LinkAddress(TEST_IPV6_ADDRESS)); + } + + @Test + public void testUpdateIpv6Upstream() throws Exception { + // 1. Add first IpServer. + final IpServer firstServer = mock(IpServer.class); + mNotifyList.add(firstServer); + mIPv6TetheringCoordinator.addActiveDownstream(firstServer, STATE_TETHERED); + verify(firstServer).sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null); + verifyNoMoreInteractions(firstServer); + + // 2. Add second IpServer and it would not have ipv6 tethering. + final IpServer secondServer = mock(IpServer.class); + mNotifyList.add(secondServer); + mIPv6TetheringCoordinator.addActiveDownstream(secondServer, STATE_LOCAL_ONLY); + verifyNoMoreInteractions(secondServer); + reset(firstServer, secondServer); + + // 3. No upstream. + mIPv6TetheringCoordinator.updateUpstreamNetworkState(null); + verify(secondServer).sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null); + reset(firstServer, secondServer); + + // 4. Update ipv6 mobile upstream. + final UpstreamNetworkState mobileUpstream = createDualStackUpstream(TRANSPORT_CELLULAR); + final ArgumentCaptor lp = ArgumentCaptor.forClass(LinkProperties.class); + mIPv6TetheringCoordinator.updateUpstreamNetworkState(mobileUpstream); + verify(firstServer).sendMessage(eq(IpServer.CMD_IPV6_TETHER_UPDATE), eq(0), eq(0), + lp.capture()); + final LinkProperties v6OnlyLink = lp.getValue(); + assertOnlyOneV6AddressAndNoV4(v6OnlyLink); + verifyNoMoreInteractions(firstServer); + verifyNoMoreInteractions(secondServer); + reset(firstServer, secondServer); + + // 5. Remove first IpServer. + mNotifyList.remove(firstServer); + mIPv6TetheringCoordinator.removeActiveDownstream(firstServer); + verify(firstServer).sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null); + verify(secondServer).sendMessage(eq(IpServer.CMD_IPV6_TETHER_UPDATE), eq(0), eq(0), + lp.capture()); + final LinkProperties localOnlyLink = lp.getValue(); + assertNotNull(localOnlyLink); + assertNotEquals(localOnlyLink, v6OnlyLink); + reset(firstServer, secondServer); + + // 6. Remove second IpServer. + mNotifyList.remove(secondServer); + mIPv6TetheringCoordinator.removeActiveDownstream(secondServer); + verifyNoMoreInteractions(firstServer); + verify(secondServer).sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null); + } +} From 47c8b0f87e214f25fa62af23d5489b1e376f2ce8 Mon Sep 17 00:00:00 2001 From: Mark Chien Date: Tue, 7 Apr 2020 16:17:49 +0000 Subject: [PATCH 086/188] Add TetheringServiceTest unitest Bug: 145490751 Test: atest TetheringTests Change-Id: Ia9e3232467c7db0e566cced84f3c72bbcd6512d6 Merged-In: I68cd403302848c041444e6d47652435d67f59273 (cherry picked from commit 790a4dd7d3aa813f15fe95c5bf3cef6dd734ce32) --- Tethering/tests/unit/AndroidManifest.xml | 9 + .../tethering/MockTetheringService.java | 56 +++++ .../tethering/TetheringServiceTest.java | 194 ++++++++++++++++++ .../connectivity/tethering/TetheringTest.java | 14 ++ 4 files changed, 273 insertions(+) create mode 100644 Tethering/tests/unit/src/com/android/server/connectivity/tethering/MockTetheringService.java create mode 100644 Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringServiceTest.java diff --git a/Tethering/tests/unit/AndroidManifest.xml b/Tethering/tests/unit/AndroidManifest.xml index 530bc0788a..4ff1d3777f 100644 --- a/Tethering/tests/unit/AndroidManifest.xml +++ b/Tethering/tests/unit/AndroidManifest.xml @@ -20,7 +20,16 @@ + + + + + + diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/MockTetheringService.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/MockTetheringService.java new file mode 100644 index 0000000000..355ece9a44 --- /dev/null +++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/MockTetheringService.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.connectivity.tethering; + +import static org.mockito.Mockito.mock; + +import android.content.Intent; +import android.net.ITetheringConnector; +import android.os.Binder; +import android.os.IBinder; + +public class MockTetheringService extends TetheringService { + private final Tethering mTethering = mock(Tethering.class); + + @Override + public IBinder onBind(Intent intent) { + return new MockTetheringConnector(super.onBind(intent)); + } + + @Override + public Tethering makeTethering(TetheringDependencies deps) { + return mTethering; + } + + public Tethering getTethering() { + return mTethering; + } + + public class MockTetheringConnector extends Binder { + final IBinder mBase; + MockTetheringConnector(IBinder base) { + mBase = base; + } + + public ITetheringConnector getTetheringConnector() { + return ITetheringConnector.Stub.asInterface(mBase); + } + + public MockTetheringService getService() { + return MockTetheringService.this; + } + } +} diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringServiceTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringServiceTest.java new file mode 100644 index 0000000000..d9d3e73eb4 --- /dev/null +++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringServiceTest.java @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.connectivity.tethering; + +import static android.net.TetheringManager.TETHERING_WIFI; +import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import android.content.Intent; +import android.net.IIntResultListener; +import android.net.ITetheringConnector; +import android.net.ITetheringEventCallback; +import android.net.TetheringRequestParcel; +import android.os.ResultReceiver; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.rule.ServiceTestRule; +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.connectivity.tethering.MockTetheringService.MockTetheringConnector; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public final class TetheringServiceTest { + private static final String TEST_IFACE_NAME = "test_wlan0"; + private static final String TEST_CALLER_PKG = "test_pkg"; + @Mock private ITetheringEventCallback mITetheringEventCallback; + @Rule public ServiceTestRule mServiceTestRule; + private Tethering mTethering; + private Intent mMockServiceIntent; + private ITetheringConnector mTetheringConnector; + + private class TestTetheringResult extends IIntResultListener.Stub { + private int mResult = -1; // Default value that does not match any result code. + @Override + public void onResult(final int resultCode) { + mResult = resultCode; + } + + public void assertResult(final int expected) { + assertEquals(expected, mResult); + } + } + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mServiceTestRule = new ServiceTestRule(); + mMockServiceIntent = new Intent( + InstrumentationRegistry.getTargetContext(), + MockTetheringService.class); + final MockTetheringConnector mockConnector = + (MockTetheringConnector) mServiceTestRule.bindService(mMockServiceIntent); + mTetheringConnector = mockConnector.getTetheringConnector(); + final MockTetheringService service = mockConnector.getService(); + mTethering = service.getTethering(); + verify(mTethering).startStateMachineUpdaters(); + when(mTethering.hasTetherableConfiguration()).thenReturn(true); + } + + @After + public void tearDown() throws Exception { + mServiceTestRule.unbindService(); + } + + @Test + public void testTether() throws Exception { + when(mTethering.tether(TEST_IFACE_NAME)).thenReturn(TETHER_ERROR_NO_ERROR); + final TestTetheringResult result = new TestTetheringResult(); + mTetheringConnector.tether(TEST_IFACE_NAME, TEST_CALLER_PKG, result); + verify(mTethering).hasTetherableConfiguration(); + verify(mTethering).tether(TEST_IFACE_NAME); + verifyNoMoreInteractions(mTethering); + result.assertResult(TETHER_ERROR_NO_ERROR); + } + + @Test + public void testUntether() throws Exception { + when(mTethering.untether(TEST_IFACE_NAME)).thenReturn(TETHER_ERROR_NO_ERROR); + final TestTetheringResult result = new TestTetheringResult(); + mTetheringConnector.untether(TEST_IFACE_NAME, TEST_CALLER_PKG, result); + verify(mTethering).hasTetherableConfiguration(); + verify(mTethering).untether(TEST_IFACE_NAME); + verifyNoMoreInteractions(mTethering); + result.assertResult(TETHER_ERROR_NO_ERROR); + } + + @Test + public void testSetUsbTethering() throws Exception { + when(mTethering.setUsbTethering(true /* enable */)).thenReturn(TETHER_ERROR_NO_ERROR); + final TestTetheringResult result = new TestTetheringResult(); + mTetheringConnector.setUsbTethering(true /* enable */, TEST_CALLER_PKG, result); + verify(mTethering).hasTetherableConfiguration(); + verify(mTethering).setUsbTethering(true /* enable */); + verifyNoMoreInteractions(mTethering); + result.assertResult(TETHER_ERROR_NO_ERROR); + } + + @Test + public void testStartTethering() throws Exception { + final TestTetheringResult result = new TestTetheringResult(); + final TetheringRequestParcel request = new TetheringRequestParcel(); + request.tetheringType = TETHERING_WIFI; + mTetheringConnector.startTethering(request, TEST_CALLER_PKG, result); + verify(mTethering).hasTetherableConfiguration(); + verify(mTethering).startTethering(eq(request), eq(result)); + verifyNoMoreInteractions(mTethering); + } + + @Test + public void testStopTethering() throws Exception { + final TestTetheringResult result = new TestTetheringResult(); + mTetheringConnector.stopTethering(TETHERING_WIFI, TEST_CALLER_PKG, result); + verify(mTethering).hasTetherableConfiguration(); + verify(mTethering).stopTethering(TETHERING_WIFI); + verifyNoMoreInteractions(mTethering); + result.assertResult(TETHER_ERROR_NO_ERROR); + } + + @Test + public void testRequestLatestTetheringEntitlementResult() throws Exception { + final ResultReceiver result = new ResultReceiver(null); + mTetheringConnector.requestLatestTetheringEntitlementResult(TETHERING_WIFI, result, + true /* showEntitlementUi */, TEST_CALLER_PKG); + verify(mTethering).hasTetherableConfiguration(); + verify(mTethering).requestLatestTetheringEntitlementResult(eq(TETHERING_WIFI), + eq(result), eq(true) /* showEntitlementUi */); + verifyNoMoreInteractions(mTethering); + } + + @Test + public void testRegisterTetheringEventCallback() throws Exception { + mTetheringConnector.registerTetheringEventCallback(mITetheringEventCallback, + TEST_CALLER_PKG); + verify(mTethering).registerTetheringEventCallback(eq(mITetheringEventCallback)); + verifyNoMoreInteractions(mTethering); + } + + @Test + public void testUnregisterTetheringEventCallback() throws Exception { + mTetheringConnector.unregisterTetheringEventCallback(mITetheringEventCallback, + TEST_CALLER_PKG); + verify(mTethering).unregisterTetheringEventCallback( + eq(mITetheringEventCallback)); + verifyNoMoreInteractions(mTethering); + } + + @Test + public void testStopAllTethering() throws Exception { + final TestTetheringResult result = new TestTetheringResult(); + mTetheringConnector.stopAllTethering(TEST_CALLER_PKG, result); + verify(mTethering).hasTetherableConfiguration(); + verify(mTethering).untetherAll(); + verifyNoMoreInteractions(mTethering); + result.assertResult(TETHER_ERROR_NO_ERROR); + } + + @Test + public void testIsTetheringSupported() throws Exception { + final TestTetheringResult result = new TestTetheringResult(); + mTetheringConnector.isTetheringSupported(TEST_CALLER_PKG, result); + verify(mTethering).hasTetherableConfiguration(); + verifyNoMoreInteractions(mTethering); + result.assertResult(TETHER_ERROR_NO_ERROR); + } +} diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java index a59c6fd9e1..3a580dd8e5 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java @@ -140,7 +140,9 @@ import com.android.networkstack.tethering.R; import com.android.testutils.MiscAssertsKt; import org.junit.After; +import org.junit.AfterClass; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -439,6 +441,18 @@ public class TetheringTest { return buildMobileUpstreamState(false, true, true); } + // See FakeSettingsProvider#clearSettingsProvider() that this needs to be called before and + // after use. + @BeforeClass + public static void setupOnce() { + FakeSettingsProvider.clearSettingsProvider(); + } + + @AfterClass + public static void tearDownOnce() { + FakeSettingsProvider.clearSettingsProvider(); + } + @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); From b33911c19c272866866689f2a1be849fd2cc4164 Mon Sep 17 00:00:00 2001 From: Automerger Merge Worker Date: Wed, 8 Apr 2020 07:13:44 +0000 Subject: [PATCH 087/188] Fix TetheringIntegrationTests failure Bug: 150644681 Test: atest TetheringIntegrationTests Change-Id: I5a537eca9b1aab3694a11a2dab147a31f289314c Merged-In: I5a537eca9b1aab3694a11a2dab147a31f289314c (cherry picked from commit f626b8a5388746d163600a5eccb60e22cb1071bf) --- Tethering/tests/integration/Android.bp | 5 +++++ .../integration/src/android/net/EthernetTetheringTest.java | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Tethering/tests/integration/Android.bp b/Tethering/tests/integration/Android.bp index 1a1c30d1d5..620261b375 100644 --- a/Tethering/tests/integration/Android.bp +++ b/Tethering/tests/integration/Android.bp @@ -39,4 +39,9 @@ android_test { "android.test.base", "android.test.mock", ], + jni_libs: [ + // For mockito extended + "libdexmakerjvmtiagent", + "libstaticjvmtiagent", + ], } diff --git a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java index dbd68ef77c..b02bb23f98 100644 --- a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java +++ b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java @@ -205,7 +205,7 @@ public class EthernetTetheringTest { requestWithStaticIpv4(localAddr, clientAddr)); mTetheringEventCallback.awaitInterfaceTethered(); - assertInterfaceHasIpAddress(iface, clientAddr); + assertInterfaceHasIpAddress(iface, localAddr); byte[] client1 = MacAddress.fromString("1:2:3:4:5:6").toByteArray(); byte[] client2 = MacAddress.fromString("a:b:c:d:e:f").toByteArray(); From fab349c0a9d20394e1c54060a58b05901e9999c9 Mon Sep 17 00:00:00 2001 From: Paul Hu Date: Wed, 8 Apr 2020 08:51:23 +0000 Subject: [PATCH 088/188] Address aosp/1274403 leftover comment Bug: 152828142 Test: atest EntitlementManagerTest Change-Id: I81f2d268c9f26bc4488b06032477a73d071f73f8 Merged-In: I81f2d268c9f26bc4488b06032477a73d071f73f8 (cherry picked from commit 9fe0a868e909a4cd1c1c63bf981441f1b9128890) --- .../tethering/EntitlementManagerTest.java | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java index b3a30abca6..6695eed0ff 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java +++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java @@ -33,7 +33,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSess import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; @@ -72,8 +71,6 @@ import org.mockito.MockitoSession; import org.mockito.quality.Strictness; import java.util.ArrayList; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; @RunWith(AndroidJUnit4.class) @SmallTest @@ -255,19 +252,16 @@ public final class EntitlementManagerTest { @Test public void testRequestLastEntitlementCacheValue() throws Exception { - final CountDownLatch mCallbacklatch = new CountDownLatch(1); // 1. Entitlement check is not required. mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; ResultReceiver receiver = new ResultReceiver(null) { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { assertEquals(TETHER_ERROR_NO_ERROR, resultCode); - mCallbacklatch.countDown(); } }; mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true); mLooper.dispatchAll(); - callbackTimeoutHelper(mCallbacklatch); assertEquals(0, mEnMgr.uiProvisionCount); mEnMgr.reset(); @@ -277,12 +271,10 @@ public final class EntitlementManagerTest { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { assertEquals(TETHER_ERROR_ENTITLEMENT_UNKNOWN, resultCode); - mCallbacklatch.countDown(); } }; mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false); mLooper.dispatchAll(); - callbackTimeoutHelper(mCallbacklatch); assertEquals(0, mEnMgr.uiProvisionCount); mEnMgr.reset(); // 3. No cache value and ui entitlement check is needed. @@ -291,12 +283,10 @@ public final class EntitlementManagerTest { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { assertEquals(TETHER_ERROR_PROVISIONING_FAILED, resultCode); - mCallbacklatch.countDown(); } }; mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true); mLooper.dispatchAll(); - callbackTimeoutHelper(mCallbacklatch); assertEquals(1, mEnMgr.uiProvisionCount); mEnMgr.reset(); // 4. Cache value is TETHER_ERROR_PROVISIONING_FAILED and don't need to run entitlement @@ -306,12 +296,10 @@ public final class EntitlementManagerTest { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { assertEquals(TETHER_ERROR_PROVISIONING_FAILED, resultCode); - mCallbacklatch.countDown(); } }; mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false); mLooper.dispatchAll(); - callbackTimeoutHelper(mCallbacklatch); assertEquals(0, mEnMgr.uiProvisionCount); mEnMgr.reset(); // 5. Cache value is TETHER_ERROR_PROVISIONING_FAILED and ui entitlement check is needed. @@ -320,12 +308,10 @@ public final class EntitlementManagerTest { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { assertEquals(TETHER_ERROR_NO_ERROR, resultCode); - mCallbacklatch.countDown(); } }; mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true); mLooper.dispatchAll(); - callbackTimeoutHelper(mCallbacklatch); assertEquals(1, mEnMgr.uiProvisionCount); mEnMgr.reset(); // 6. Cache value is TETHER_ERROR_NO_ERROR. @@ -334,12 +320,10 @@ public final class EntitlementManagerTest { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { assertEquals(TETHER_ERROR_NO_ERROR, resultCode); - mCallbacklatch.countDown(); } }; mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true); mLooper.dispatchAll(); - callbackTimeoutHelper(mCallbacklatch); assertEquals(0, mEnMgr.uiProvisionCount); mEnMgr.reset(); // 7. Test get value for other downstream type. @@ -347,12 +331,10 @@ public final class EntitlementManagerTest { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { assertEquals(TETHER_ERROR_ENTITLEMENT_UNKNOWN, resultCode); - mCallbacklatch.countDown(); } }; mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_USB, receiver, false); mLooper.dispatchAll(); - callbackTimeoutHelper(mCallbacklatch); assertEquals(0, mEnMgr.uiProvisionCount); mEnMgr.reset(); // 8. Test get value for invalid downstream type. @@ -361,22 +343,14 @@ public final class EntitlementManagerTest { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { assertEquals(TETHER_ERROR_ENTITLEMENT_UNKNOWN, resultCode); - mCallbacklatch.countDown(); } }; mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI_P2P, receiver, true); mLooper.dispatchAll(); - callbackTimeoutHelper(mCallbacklatch); assertEquals(0, mEnMgr.uiProvisionCount); mEnMgr.reset(); } - void callbackTimeoutHelper(final CountDownLatch latch) throws Exception { - if (!latch.await(1, TimeUnit.SECONDS)) { - fail("Timout, fail to receive callback"); - } - } - @Test public void verifyPermissionResult() { setupForRequiredProvisioning(); From d20078112bb72bbb4c16c4d1b8463fe451ebb750 Mon Sep 17 00:00:00 2001 From: Luke Huang Date: Tue, 7 Apr 2020 15:45:02 +0000 Subject: [PATCH 089/188] Use the lastest frozen netd_aidl_interface in framework Bug: 140541991 Test: build Merged-In: I984969e09f8d5196945a7412c51bd8880223ad9e Change-Id: I984969e09f8d5196945a7412c51bd8880223ad9e --- Tethering/Android.bp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tethering/Android.bp b/Tethering/Android.bp index 190b443227..5b052df75e 100644 --- a/Tethering/Android.bp +++ b/Tethering/Android.bp @@ -25,7 +25,7 @@ java_defaults { ], static_libs: [ "androidx.annotation_annotation", - "netd_aidl_interface-unstable-java", + "netd_aidl_interface-V3-java", "netlink-client", "networkstack-aidl-interfaces-unstable-java", "android.hardware.tetheroffload.config-V1.0-java", From 5da1070d8fdd17a5150bdc37c8d3af4938acf5f1 Mon Sep 17 00:00:00 2001 From: Treehugger Robot Date: Mon, 30 Mar 2020 04:23:55 +0000 Subject: [PATCH 090/188] Assign specific client address to dhcp server Bug: 141256482 Test: manual atest TetheringTests Merged-In: Ief76c98c843ba5420224cbf0f34464f366c891b7 Change-Id: Ief76c98c843ba5420224cbf0f34464f366c891b7 --- .../src/android/net/TetheringManager.java | 23 +++++++++---- .../net/dhcp/DhcpServingParamsParcelExt.java | 15 ++++++++- Tethering/src/android/net/ip/IpServer.java | 32 ++++++++++++++----- .../dhcp/DhcpServingParamsParcelExtTest.java | 8 +++++ .../connectivity/tethering/TetheringTest.java | 20 ++++++++---- 5 files changed, 76 insertions(+), 22 deletions(-) diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java index 350980137f..cc095a0bb4 100644 --- a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java +++ b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java @@ -571,9 +571,8 @@ public class TetheringManager { /** * Configure tethering with static IPv4 assignment. * - * The clientAddress must be in the localIPv4Address prefix. A DHCP server will be - * started, but will only be able to offer the client address. The two addresses must - * be in the same prefix. + * A DHCP server will be started, but will only be able to offer the client address. + * The two addresses must be in the same prefix. * * @param localIPv4Address The preferred local IPv4 link address to use. * @param clientAddress The static client address. @@ -584,10 +583,7 @@ public class TetheringManager { @NonNull final LinkAddress clientAddress) { Objects.requireNonNull(localIPv4Address); Objects.requireNonNull(clientAddress); - if (localIPv4Address.getPrefixLength() != clientAddress.getPrefixLength() - || !localIPv4Address.isIpv4() || !clientAddress.isIpv4() - || !new IpPrefix(localIPv4Address.toString()).equals( - new IpPrefix(clientAddress.toString()))) { + if (!checkStaticAddressConfiguration(localIPv4Address, clientAddress)) { throw new IllegalArgumentException("Invalid server or client addresses"); } @@ -656,6 +652,19 @@ public class TetheringManager { return mRequestParcel.showProvisioningUi; } + /** + * Check whether the two addresses are ipv4 and in the same prefix. + * @hide + */ + public static boolean checkStaticAddressConfiguration( + @NonNull final LinkAddress localIPv4Address, + @NonNull final LinkAddress clientAddress) { + return localIPv4Address.getPrefixLength() == clientAddress.getPrefixLength() + && localIPv4Address.isIpv4() && clientAddress.isIpv4() + && new IpPrefix(localIPv4Address.toString()).equals( + new IpPrefix(clientAddress.toString())); + } + /** * Get a TetheringRequestParcel from the configuration * @hide diff --git a/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java b/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java index d6bc063210..82a26beada 100644 --- a/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java +++ b/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java @@ -18,10 +18,12 @@ package android.net.dhcp; import static android.net.shared.Inet4AddressUtils.inet4AddressToIntHTH; -import android.annotation.NonNull; import android.net.LinkAddress; import android.util.ArraySet; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import java.net.Inet4Address; import java.util.Collection; import java.util.Collections; @@ -160,6 +162,17 @@ public class DhcpServingParamsParcelExt extends DhcpServingParamsParcel { return this; } + /** + * Set the client address to tell DHCP server only offer this address. + * The client's prefix length is the same as server's. + * + *

If not set, the default value is null. + */ + public DhcpServingParamsParcelExt setSingleClientAddr(@Nullable Inet4Address clientAddr) { + this.clientAddr = clientAddr == null ? 0 : inet4AddressToIntHTH(clientAddr); + return this; + } + private static int[] toIntArray(@NonNull Collection addrs) { int[] res = new int[addrs.size()]; int i = 0; diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java index 5b6fe91b9c..1dac5b7846 100644 --- a/Tethering/src/android/net/ip/IpServer.java +++ b/Tethering/src/android/net/ip/IpServer.java @@ -18,6 +18,7 @@ package android.net.ip; import static android.net.InetAddresses.parseNumericAddress; import static android.net.RouteInfo.RTN_UNICAST; +import static android.net.TetheringManager.TetheringRequest.checkStaticAddressConfiguration; import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS; import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH; import static android.net.util.NetworkConstants.FF; @@ -511,17 +512,24 @@ public class IpServer extends StateMachine { } } - private boolean startDhcp(Inet4Address addr, int prefixLen) { + private boolean startDhcp(final LinkAddress serverLinkAddr, final LinkAddress clientLinkAddr) { if (mUsingLegacyDhcp) { return true; } + + final Inet4Address addr = (Inet4Address) serverLinkAddr.getAddress(); + final int prefixLen = serverLinkAddr.getPrefixLength(); + final Inet4Address clientAddr = clientLinkAddr == null ? null : + (Inet4Address) clientLinkAddr.getAddress(); + final DhcpServingParamsParcel params; params = new DhcpServingParamsParcelExt() .setDefaultRouters(addr) .setDhcpLeaseTimeSecs(DHCP_LEASE_TIME_SECS) .setDnsServers(addr) - .setServerAddr(new LinkAddress(addr, prefixLen)) - .setMetered(true); + .setServerAddr(serverLinkAddr) + .setMetered(true) + .setSingleClientAddr(clientAddr); // TODO: also advertise link MTU mDhcpServerStartIndex++; @@ -556,9 +564,10 @@ public class IpServer extends StateMachine { } } - private boolean configureDhcp(boolean enable, Inet4Address addr, int prefixLen) { + private boolean configureDhcp(boolean enable, final LinkAddress serverAddr, + final LinkAddress clientAddr) { if (enable) { - return startDhcp(addr, prefixLen); + return startDhcp(serverAddr, clientAddr); } else { stopDhcp(); return true; @@ -606,7 +615,7 @@ public class IpServer extends StateMachine { // code that calls into NetworkManagementService directly. srvAddr = (Inet4Address) parseNumericAddress(BLUETOOTH_IFACE_ADDR); mIpv4Address = new LinkAddress(srvAddr, BLUETOOTH_DHCP_PREFIX_LENGTH); - return configureDhcp(enabled, srvAddr, BLUETOOTH_DHCP_PREFIX_LENGTH); + return configureDhcp(enabled, mIpv4Address, null /* clientAddress */); } mIpv4Address = new LinkAddress(srvAddr, prefixLen); } catch (IllegalArgumentException e) { @@ -643,7 +652,7 @@ public class IpServer extends StateMachine { mLinkProperties.removeRoute(route); } - return configureDhcp(enabled, srvAddr, prefixLen); + return configureDhcp(enabled, mIpv4Address, mStaticIpv4ClientAddr); } private String getRandomWifiIPv4Address() { @@ -962,7 +971,14 @@ public class IpServer extends StateMachine { } private void maybeConfigureStaticIp(final TetheringRequestParcel request) { - if (request == null) return; + // Ignore static address configuration if they are invalid or null. In theory, static + // addresses should not be invalid here because TetheringManager do not allow caller to + // specify invalid static address configuration. + if (request == null || request.localIPv4Address == null + || request.staticClientAddress == null || !checkStaticAddressConfiguration( + request.localIPv4Address, request.staticClientAddress)) { + return; + } mStaticIpv4ServerAddr = request.localIPv4Address; mStaticIpv4ClientAddr = request.staticClientAddress; diff --git a/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java b/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java index e8add9830b..f8eb1476ba 100644 --- a/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java +++ b/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java @@ -42,7 +42,9 @@ import java.util.stream.IntStream; @SmallTest public class DhcpServingParamsParcelExtTest { private static final Inet4Address TEST_ADDRESS = inet4Addr("192.168.0.123"); + private static final Inet4Address TEST_CLIENT_ADDRESS = inet4Addr("192.168.0.42"); private static final int TEST_ADDRESS_PARCELED = 0xc0a8007b; + private static final int TEST_CLIENT_ADDRESS_PARCELED = 0xc0a8002a; private static final int TEST_PREFIX_LENGTH = 17; private static final int TEST_LEASE_TIME_SECS = 120; private static final int TEST_MTU = 1000; @@ -105,6 +107,12 @@ public class DhcpServingParamsParcelExtTest { assertFalse(mParcel.metered); } + @Test + public void testSetClientAddr() { + mParcel.setSingleClientAddr(TEST_CLIENT_ADDRESS); + assertEquals(TEST_CLIENT_ADDRESS_PARCELED, mParcel.clientAddr); + } + private static Inet4Address inet4Addr(String addr) { return (Inet4Address) parseNumericAddress(addr); } diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java index 3a580dd8e5..2955903c84 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java @@ -38,6 +38,7 @@ 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.dhcp.IDhcpServer.STATUS_SUCCESS; +import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE; @@ -1668,10 +1669,13 @@ public class TetheringTest { } @Test - public void testRequestStaticServerIp() throws Exception { - final LinkAddress serverLinkAddr = new LinkAddress("192.168.20.1/24"); - final LinkAddress clientLinkAddr = new LinkAddress("192.168.20.42/24"); - final String serverAddr = "192.168.20.1"; + public void testRequestStaticIp() throws Exception { + final LinkAddress serverLinkAddr = new LinkAddress("192.168.0.123/24"); + final LinkAddress clientLinkAddr = new LinkAddress("192.168.0.42/24"); + final String serverAddr = "192.168.0.123"; + final int clientAddrParceled = 0xc0a8002a; + final ArgumentCaptor dhcpParamsCaptor = + ArgumentCaptor.forClass(DhcpServingParamsParcel.class); mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB, serverLinkAddr, clientLinkAddr), null); mLooper.dispatchAll(); @@ -1680,8 +1684,12 @@ public class TetheringTest { sendUsbBroadcast(true, true, true, TETHERING_USB); mLooper.dispatchAll(); verify(mNetd).interfaceSetCfg(argThat(cfg -> serverAddr.equals(cfg.ipv4Addr))); - - // TODO: test static client address. + verify(mIpServerDependencies, times(1)).makeDhcpServer(any(), dhcpParamsCaptor.capture(), + any()); + final DhcpServingParamsParcel params = dhcpParamsCaptor.getValue(); + assertEquals(serverAddr, intToInet4AddressHTH(params.serverAddr).getHostAddress()); + assertEquals(24, params.serverAddrPrefixLength); + assertEquals(clientAddrParceled, params.clientAddr); } // TODO: Test that a request for hotspot mode doesn't interfere with an From 6c9c10c91efa5efacd0b3af61be3e623c3682df0 Mon Sep 17 00:00:00 2001 From: Jiyong Park Date: Mon, 13 Apr 2020 12:55:24 +0900 Subject: [PATCH 091/188] Mark some aidl_interface modules as unstable With b/152655547, all aidl_interface modules are considered as stable unless it is explicitly with "unstable: true". This change marks the aidl_interface that are not used across updatable module bounraries as unstable, so that the build system does not run the API dumping/checking on them. Bug: 152655547 Test: m Change-Id: I1257c66de6dd42b2d32d47ed74cb2878f79d14fb --- Tethering/common/TetheringLib/Android.bp | 1 + 1 file changed, 1 insertion(+) diff --git a/Tethering/common/TetheringLib/Android.bp b/Tethering/common/TetheringLib/Android.bp index 31c40d2a33..ee6b9f12f9 100644 --- a/Tethering/common/TetheringLib/Android.bp +++ b/Tethering/common/TetheringLib/Android.bp @@ -16,6 +16,7 @@ // AIDL interfaces between the core system and the tethering mainline module. aidl_interface { name: "tethering-aidl-interfaces", + unstable: true, local_include_dir: "src", include_dirs: ["frameworks/base/core/java"], // For framework parcelables. srcs: [ From af37b0a7232783566403e045f26a6d111e1c1462 Mon Sep 17 00:00:00 2001 From: Ashwini Oruganti Date: Tue, 10 Mar 2020 13:49:18 -0700 Subject: [PATCH 092/188] Tethering: Add an exported flag in manifest With b/150232615, we will need an explicit value set for the exported flag when intent filters are present, as the default behavior is changing for S+. This change adds the value reflecting the previous default to the manifest. Bug: 150232615 Test: TH Change-Id: I25b55378df393cd4fb8932b7ae64f97eb9f1aa8e Merged-In: I25b55378df393cd4fb8932b7ae64f97eb9f1aa8e (cherry picked from commit 9226d6c835cd57d10a7562f18143dbaae8601b43) --- Tethering/AndroidManifest.xml | 3 ++- Tethering/AndroidManifest_InProcess.xml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Tethering/AndroidManifest.xml b/Tethering/AndroidManifest.xml index 9328611f5d..ab257475ca 100644 --- a/Tethering/AndroidManifest.xml +++ b/Tethering/AndroidManifest.xml @@ -44,7 +44,8 @@ android:extractNativeLibs="false" android:persistent="true"> + android:permission="android.permission.MAINLINE_NETWORK_STACK" + android:exported="true"> diff --git a/Tethering/AndroidManifest_InProcess.xml b/Tethering/AndroidManifest_InProcess.xml index 02ea551254..bf1f001e03 100644 --- a/Tethering/AndroidManifest_InProcess.xml +++ b/Tethering/AndroidManifest_InProcess.xml @@ -24,7 +24,8 @@ + android:permission="android.permission.MAINLINE_NETWORK_STACK" + android:exported="true"> From 7dc2dcc37aca88dca1890ecf82baf989ddc52f28 Mon Sep 17 00:00:00 2001 From: markchien Date: Sun, 12 Apr 2020 21:41:29 +0800 Subject: [PATCH 093/188] Change tethering file structure to respect its package name Bug: 145099347 Test: atest TetheringTests atest CtsTetheringTest Change-Id: I7401c550fbafd17a5ed2d925b5d0e17e358af467 Merged-In: I7401c550fbafd17a5ed2d925b5d0e17e358af467 --- Tethering/AndroidManifest.xml | 2 +- Tethering/AndroidManifest_InProcess.xml | 2 +- Tethering/proguard.flags | 2 +- Tethering/res/values/config.xml | 2 +- .../tethering/ConnectedClientsTracker.java | 2 +- .../tethering/EntitlementManager.java | 5 ++--- .../tethering/IPv6TetheringCoordinator.java | 2 +- .../tethering/OffloadController.java | 4 ++-- .../tethering/OffloadHardwareInterface.java | 2 +- .../tethering/Tethering.java | 4 ++-- .../tethering/TetheringConfiguration.java | 3 +-- .../tethering/TetheringDependencies.java | 2 +- .../tethering/TetheringInterfaceUtils.java | 2 +- .../tethering/TetheringNotificationUpdater.java | 3 +-- .../tethering/TetheringService.java | 2 +- .../tethering/UpstreamNetworkMonitor.java | 2 +- .../tethering/UpstreamNetworkState.java | 2 +- Tethering/tests/unit/AndroidManifest.xml | 4 ++-- .../tethering/ConnectedClientsTrackerTest.kt | 4 ++-- .../tethering/EntitlementManagerTest.java | 3 +-- .../tethering/IPv6TetheringCoordinatorTest.java | 2 +- .../tethering/MockTetheringService.java | 2 +- .../tethering/OffloadControllerTest.java | 8 ++++---- .../tethering/TetheringConfigurationTest.java | 3 +-- .../tethering/TetheringNotificationUpdaterTest.kt | 7 +++---- .../tethering/TetheringServiceTest.java | 4 ++-- .../tethering/TetheringTest.java | 5 ++--- .../tethering/UpstreamNetworkMonitorTest.java | 4 ++-- 28 files changed, 41 insertions(+), 48 deletions(-) rename Tethering/src/com/android/{server/connectivity => networkstack}/tethering/ConnectedClientsTracker.java (99%) rename Tethering/src/com/android/{server/connectivity => networkstack}/tethering/EntitlementManager.java (99%) rename Tethering/src/com/android/{server/connectivity => networkstack}/tethering/IPv6TetheringCoordinator.java (99%) rename Tethering/src/com/android/{server/connectivity => networkstack}/tethering/OffloadController.java (99%) rename Tethering/src/com/android/{server/connectivity => networkstack}/tethering/OffloadHardwareInterface.java (99%) rename Tethering/src/com/android/{server/connectivity => networkstack}/tethering/Tethering.java (99%) rename Tethering/src/com/android/{server/connectivity => networkstack}/tethering/TetheringConfiguration.java (99%) rename Tethering/src/com/android/{server/connectivity => networkstack}/tethering/TetheringDependencies.java (98%) rename Tethering/src/com/android/{server/connectivity => networkstack}/tethering/TetheringInterfaceUtils.java (98%) rename Tethering/src/com/android/{server/connectivity => networkstack}/tethering/TetheringNotificationUpdater.java (99%) rename Tethering/src/com/android/{server/connectivity => networkstack}/tethering/TetheringService.java (99%) rename Tethering/src/com/android/{server/connectivity => networkstack}/tethering/UpstreamNetworkMonitor.java (99%) rename Tethering/src/com/android/{server/connectivity => networkstack}/tethering/UpstreamNetworkState.java (97%) rename Tethering/tests/unit/src/com/android/{server/connectivity => networkstack}/tethering/ConnectedClientsTrackerTest.kt (99%) rename Tethering/tests/unit/src/com/android/{server/connectivity => networkstack}/tethering/EntitlementManagerTest.java (99%) rename Tethering/tests/unit/src/com/android/{server/connectivity => networkstack}/tethering/IPv6TetheringCoordinatorTest.java (99%) rename Tethering/tests/unit/src/com/android/{server/connectivity => networkstack}/tethering/MockTetheringService.java (96%) rename Tethering/tests/unit/src/com/android/{server/connectivity => networkstack}/tethering/OffloadControllerTest.java (99%) rename Tethering/tests/unit/src/com/android/{server/connectivity => networkstack}/tethering/TetheringConfigurationTest.java (99%) rename Tethering/tests/unit/src/com/android/{server/connectivity => networkstack}/tethering/TetheringNotificationUpdaterTest.kt (98%) rename Tethering/tests/unit/src/com/android/{server/connectivity => networkstack}/tethering/TetheringServiceTest.java (98%) rename Tethering/tests/unit/src/com/android/{server/connectivity => networkstack}/tethering/TetheringTest.java (99%) rename Tethering/tests/unit/src/com/android/{server/connectivity => networkstack}/tethering/UpstreamNetworkMonitorTest.java (99%) diff --git a/Tethering/AndroidManifest.xml b/Tethering/AndroidManifest.xml index ab257475ca..1dc8227e81 100644 --- a/Tethering/AndroidManifest.xml +++ b/Tethering/AndroidManifest.xml @@ -43,7 +43,7 @@ android:process="com.android.networkstack.process" android:extractNativeLibs="false" android:persistent="true"> - diff --git a/Tethering/AndroidManifest_InProcess.xml b/Tethering/AndroidManifest_InProcess.xml index bf1f001e03..b1f124097c 100644 --- a/Tethering/AndroidManifest_InProcess.xml +++ b/Tethering/AndroidManifest_InProcess.xml @@ -22,7 +22,7 @@ android:process="system"> - diff --git a/Tethering/proguard.flags b/Tethering/proguard.flags index 1f83a66382..051fbd19fc 100644 --- a/Tethering/proguard.flags +++ b/Tethering/proguard.flags @@ -1,5 +1,5 @@ # Keep class's integer static field for MessageUtils to parsing their name. --keep class com.android.server.connectivity.tethering.Tethering$TetherMasterSM { +-keep class com.android.networkstack.tethering.Tethering$TetherMasterSM { static final int CMD_*; static final int EVENT_*; } diff --git a/Tethering/res/values/config.xml b/Tethering/res/values/config.xml index 04d6215dce..430fdc4228 100644 --- a/Tethering/res/values/config.xml +++ b/Tethering/res/values/config.xml @@ -89,7 +89,7 @@ TYPE_MOBILE_HIPRI is appended. For other changes applied to this list, now and in the future, see - com.android.server.connectivity.tethering.TetheringConfiguration. + com.android.networkstack.tethering.TetheringConfiguration. Note also: the order of this is important. The first upstream type for which a satisfying network exists is used. diff --git a/Tethering/src/com/android/server/connectivity/tethering/ConnectedClientsTracker.java b/Tethering/src/com/android/networkstack/tethering/ConnectedClientsTracker.java similarity index 99% rename from Tethering/src/com/android/server/connectivity/tethering/ConnectedClientsTracker.java rename to Tethering/src/com/android/networkstack/tethering/ConnectedClientsTracker.java index cdd1a5d978..8a96988ae1 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/ConnectedClientsTracker.java +++ b/Tethering/src/com/android/networkstack/tethering/ConnectedClientsTracker.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import static android.net.TetheringManager.TETHERING_WIFI; diff --git a/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java b/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java similarity index 99% rename from Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java rename to Tethering/src/com/android/networkstack/tethering/EntitlementManager.java index 639cf65d79..4c7b2d49ee 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java +++ b/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import static android.net.TetheringConstants.EXTRA_ADD_TETHER_TYPE; import static android.net.TetheringConstants.EXTRA_PROVISION_CALLBACK; @@ -52,7 +52,6 @@ import android.util.SparseIntArray; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.StateMachine; -import com.android.networkstack.tethering.R; import java.io.PrintWriter; @@ -71,7 +70,7 @@ public class EntitlementManager { @VisibleForTesting protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning"; private static final String ACTION_PROVISIONING_ALARM = - "com.android.server.connectivity.tethering.PROVISIONING_RECHECK_ALARM"; + "com.android.networkstack.tethering.PROVISIONING_RECHECK_ALARM"; private static final String EXTRA_SUBID = "subId"; private final ComponentName mSilentProvisioningService; diff --git a/Tethering/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java b/Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java similarity index 99% rename from Tethering/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java rename to Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java index 66b9ade810..d450c46de7 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java +++ b/Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import android.net.IpPrefix; import android.net.LinkAddress; diff --git a/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java b/Tethering/src/com/android/networkstack/tethering/OffloadController.java similarity index 99% rename from Tethering/src/com/android/server/connectivity/tethering/OffloadController.java rename to Tethering/src/com/android/networkstack/tethering/OffloadController.java index 15cdb6ad7a..c007c174fe 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java +++ b/Tethering/src/com/android/networkstack/tethering/OffloadController.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import static android.net.NetworkStats.DEFAULT_NETWORK_NO; import static android.net.NetworkStats.METERED_NO; @@ -50,7 +50,7 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IndentingPrintWriter; -import com.android.server.connectivity.tethering.OffloadHardwareInterface.ForwardedStats; +import com.android.networkstack.tethering.OffloadHardwareInterface.ForwardedStats; import java.net.Inet4Address; import java.net.Inet6Address; diff --git a/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java b/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java similarity index 99% rename from Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java rename to Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java index b545717208..85a23fb83f 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java +++ b/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import static android.net.util.TetheringUtils.uint16; diff --git a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java similarity index 99% rename from Tethering/src/com/android/server/connectivity/tethering/Tethering.java rename to Tethering/src/com/android/networkstack/tethering/Tethering.java index 4b2c9215f7..f3cead92be 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java +++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import static android.Manifest.permission.NETWORK_SETTINGS; import static android.Manifest.permission.NETWORK_STACK; @@ -60,7 +60,7 @@ import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED; import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; -import static com.android.server.connectivity.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE; +import static com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE; import android.app.usage.NetworkStatsManager; import android.bluetooth.BluetoothAdapter; diff --git a/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java similarity index 99% rename from Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java rename to Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java index 7e9e26f5af..aeac437e24 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import static android.content.Context.TELEPHONY_SERVICE; import static android.net.ConnectivityManager.TYPE_ETHERNET; @@ -33,7 +33,6 @@ import android.telephony.TelephonyManager; import android.text.TextUtils; import com.android.internal.annotations.VisibleForTesting; -import com.android.networkstack.tethering.R; import java.io.PrintWriter; import java.util.ArrayList; diff --git a/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java b/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java similarity index 98% rename from Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java rename to Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java index 0330dad6a1..893c5823dc 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import android.bluetooth.BluetoothAdapter; import android.content.Context; diff --git a/Tethering/src/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java b/Tethering/src/com/android/networkstack/tethering/TetheringInterfaceUtils.java similarity index 98% rename from Tethering/src/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java rename to Tethering/src/com/android/networkstack/tethering/TetheringInterfaceUtils.java index 4dd68301f9..ff38f717a1 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringInterfaceUtils.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import android.annotation.Nullable; import android.net.LinkProperties; diff --git a/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java b/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java similarity index 99% rename from Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java rename to Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java index 992cdd8de6..42870560cb 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import static android.net.TetheringManager.TETHERING_BLUETOOTH; import static android.net.TetheringManager.TETHERING_USB; @@ -41,7 +41,6 @@ import androidx.annotation.IntRange; import androidx.annotation.NonNull; import com.android.internal.annotations.VisibleForTesting; -import com.android.networkstack.tethering.R; /** * A class to display tethering-related notifications. diff --git a/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java b/Tethering/src/com/android/networkstack/tethering/TetheringService.java similarity index 99% rename from Tethering/src/com/android/server/connectivity/tethering/TetheringService.java rename to Tethering/src/com/android/networkstack/tethering/TetheringService.java index c30be25dbd..3ed211520d 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringService.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.net.TetheringManager.TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION; diff --git a/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java similarity index 99% rename from Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java rename to Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java index 45bb4ab6e5..25ddce4404 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java +++ b/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import static android.net.ConnectivityManager.TYPE_BLUETOOTH; import static android.net.ConnectivityManager.TYPE_ETHERNET; diff --git a/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkState.java b/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkState.java similarity index 97% rename from Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkState.java rename to Tethering/src/com/android/networkstack/tethering/UpstreamNetworkState.java index 68bb837593..bab9f84cf7 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkState.java +++ b/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkState.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import android.net.LinkProperties; import android.net.Network; diff --git a/Tethering/tests/unit/AndroidManifest.xml b/Tethering/tests/unit/AndroidManifest.xml index 4ff1d3777f..55640db693 100644 --- a/Tethering/tests/unit/AndroidManifest.xml +++ b/Tethering/tests/unit/AndroidManifest.xml @@ -21,11 +21,11 @@ - + diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/ConnectedClientsTrackerTest.kt b/Tethering/tests/unit/src/com/android/networkstack/tethering/ConnectedClientsTrackerTest.kt similarity index 99% rename from Tethering/tests/unit/src/com/android/server/connectivity/tethering/ConnectedClientsTrackerTest.kt rename to Tethering/tests/unit/src/com/android/networkstack/tethering/ConnectedClientsTrackerTest.kt index 1cdc3bbb99..d915354b0c 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/ConnectedClientsTrackerTest.kt +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/ConnectedClientsTrackerTest.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering +package com.android.networkstack.tethering import android.net.LinkAddress import android.net.MacAddress @@ -159,4 +159,4 @@ class ConnectedClientsTrackerTest { return time } } -} \ No newline at end of file +} diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java similarity index 99% rename from Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java rename to Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java index 6695eed0ff..8bd0edc249 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import static android.net.TetheringManager.TETHERING_BLUETOOTH; import static android.net.TetheringManager.TETHERING_ETHERNET; @@ -59,7 +59,6 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.State; import com.android.internal.util.StateMachine; import com.android.internal.util.test.BroadcastInterceptingContext; -import com.android.networkstack.tethering.R; import org.junit.After; import org.junit.Before; diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinatorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/IPv6TetheringCoordinatorTest.java similarity index 99% rename from Tethering/tests/unit/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinatorTest.java rename to Tethering/tests/unit/src/com/android/networkstack/tethering/IPv6TetheringCoordinatorTest.java index 912124357c..820f255145 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinatorTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/IPv6TetheringCoordinatorTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.RouteInfo.RTN_UNICAST; diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/MockTetheringService.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java similarity index 96% rename from Tethering/tests/unit/src/com/android/server/connectivity/tethering/MockTetheringService.java rename to Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java index 355ece9a44..1c81c1247d 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/MockTetheringService.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import static org.mockito.Mockito.mock; diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java similarity index 99% rename from Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java rename to Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java index fe840864fb..65797200fa 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import static android.net.NetworkStats.DEFAULT_NETWORK_NO; import static android.net.NetworkStats.METERED_NO; @@ -26,9 +26,9 @@ import static android.net.NetworkStats.UID_TETHERING; import static android.net.RouteInfo.RTN_UNICAST; import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED; -import static com.android.server.connectivity.tethering.OffloadController.StatsType.STATS_PER_IFACE; -import static com.android.server.connectivity.tethering.OffloadController.StatsType.STATS_PER_UID; -import static com.android.server.connectivity.tethering.OffloadHardwareInterface.ForwardedStats; +import static com.android.networkstack.tethering.OffloadController.StatsType.STATS_PER_IFACE; +import static com.android.networkstack.tethering.OffloadController.StatsType.STATS_PER_UID; +import static com.android.networkstack.tethering.OffloadHardwareInterface.ForwardedStats; import static com.android.testutils.MiscAssertsKt.assertContainsAll; import static com.android.testutils.MiscAssertsKt.assertThrows; import static com.android.testutils.NetworkStatsUtilsKt.orderInsensitiveEquals; diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java similarity index 99% rename from Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java rename to Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java index 3635964dd6..07ddea43f4 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import static android.net.ConnectivityManager.TYPE_ETHERNET; import static android.net.ConnectivityManager.TYPE_MOBILE; @@ -44,7 +44,6 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.test.BroadcastInterceptingContext; -import com.android.networkstack.tethering.R; import org.junit.After; import org.junit.Before; diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringNotificationUpdaterTest.kt b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt similarity index 98% rename from Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringNotificationUpdaterTest.kt rename to Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt index b86949185c..7bff74b25d 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringNotificationUpdaterTest.kt +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering +package com.android.networkstack.tethering import android.app.Notification import android.app.NotificationManager @@ -29,8 +29,7 @@ import androidx.test.platform.app.InstrumentationRegistry import androidx.test.filters.SmallTest import androidx.test.runner.AndroidJUnit4 import com.android.internal.util.test.BroadcastInterceptingContext -import com.android.networkstack.tethering.R -import com.android.server.connectivity.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE +import com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test @@ -259,4 +258,4 @@ class TetheringNotificationUpdaterTest { notificationUpdater.notifyTetheringDisabledByRestriction() verifyNotification(R.drawable.stat_sys_tether_general, disallowTitle, disallowMessage) } -} \ No newline at end of file +} diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringServiceTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java similarity index 98% rename from Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringServiceTest.java rename to Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java index d9d3e73eb4..51bad9af23 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringServiceTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import static android.net.TetheringManager.TETHERING_WIFI; import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; @@ -37,7 +37,7 @@ import androidx.test.filters.SmallTest; import androidx.test.rule.ServiceTestRule; import androidx.test.runner.AndroidJUnit4; -import com.android.server.connectivity.tethering.MockTetheringService.MockTetheringConnector; +import com.android.networkstack.tethering.MockTetheringService.MockTetheringConnector; import org.junit.After; import org.junit.Before; diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java similarity index 99% rename from Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java rename to Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java index 2955903c84..d4be3a26d9 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import static android.hardware.usb.UsbManager.USB_CONFIGURED; import static android.hardware.usb.UsbManager.USB_CONNECTED; @@ -47,7 +47,7 @@ import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED; import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; -import static com.android.server.connectivity.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE; +import static com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -137,7 +137,6 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.StateMachine; import com.android.internal.util.test.BroadcastInterceptingContext; import com.android.internal.util.test.FakeSettingsProvider; -import com.android.networkstack.tethering.R; import com.android.testutils.MiscAssertsKt; import org.junit.After; diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java similarity index 99% rename from Tethering/tests/unit/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java rename to Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java index 7c98f626a4..232588c7ee 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import static android.net.ConnectivityManager.TYPE_MOBILE_DUN; import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI; @@ -24,7 +24,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; -import static com.android.server.connectivity.tethering.UpstreamNetworkMonitor.TYPE_NONE; +import static com.android.networkstack.tethering.UpstreamNetworkMonitor.TYPE_NONE; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; From 6f532ba30fbfebd40698d6af0e793a1947b5d280 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Tue, 14 Apr 2020 09:21:19 +0000 Subject: [PATCH 094/188] Don't crash when receiving an RTM_DELNEIGH or NUD_FAILED. These events don't have MAC addresses, so the code attempts to create an Ipv6ForwardingRule with a null MAC address. This crashes when attempting to get the raw MAC address bytes to send to netd in the TetherOffloadRuleParcel. This was not caught by unit tests because the test exercise this code path in a way that is not correct (by sending RTM_DELNEIGH and NUD_FAILED events with MAC addresses). Fix the unit tests to properly pass in null MAC addresses for these events. Bug: 153697068 Test: fixed existing tests to be more realistic Merged-In: I26d89a81f1c448d9b4809652b079a5f5eace3924 Change-Id: I26d89a81f1c448d9b4809652b079a5f5eace3924 --- Tethering/src/android/net/ip/IpServer.java | 9 +++++++-- .../tests/unit/src/android/net/ip/IpServerTest.java | 8 +++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java index 1dac5b7846..83727bcdc6 100644 --- a/Tethering/src/android/net/ip/IpServer.java +++ b/Tethering/src/android/net/ip/IpServer.java @@ -122,6 +122,8 @@ public class IpServer extends StateMachine { // TODO: have this configurable private static final int DHCP_LEASE_TIME_SECS = 3600; + private static final MacAddress NULL_MAC_ADDRESS = MacAddress.fromString("00:00:00:00:00:00"); + private static final String TAG = "IpServer"; private static final boolean DBG = false; private static final boolean VDBG = false; @@ -902,9 +904,12 @@ public class IpServer extends StateMachine { return; } + // When deleting rules, we still need to pass a non-null MAC, even though it's ignored. + // Do this here instead of in the Ipv6ForwardingRule constructor to ensure that we never + // add rules with a null MAC, only delete them. + MacAddress dstMac = e.isValid() ? e.macAddr : NULL_MAC_ADDRESS; Ipv6ForwardingRule rule = new Ipv6ForwardingRule(upstreamIfindex, - mInterfaceParams.index, (Inet6Address) e.ip, mInterfaceParams.macAddr, - e.macAddr); + mInterfaceParams.index, (Inet6Address) e.ip, mInterfaceParams.macAddr, dstMac); if (e.isValid()) { addIpv6ForwardingRule(rule); } else { diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java index fdfdae837d..f9be7b9d36 100644 --- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java +++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java @@ -587,6 +587,7 @@ public class IpServerTest { final InetAddress neighB = InetAddresses.parseNumericAddress("2001:db8::2"); final InetAddress neighLL = InetAddresses.parseNumericAddress("fe80::1"); final InetAddress neighMC = InetAddresses.parseNumericAddress("ff02::1234"); + final MacAddress macNull = MacAddress.fromString("00:00:00:00:00:00"); final MacAddress macA = MacAddress.fromString("00:00:00:00:00:0a"); final MacAddress macB = MacAddress.fromString("11:22:33:00:00:0b"); @@ -612,13 +613,14 @@ public class IpServerTest { verifyNoMoreInteractions(mNetd); // A neighbor that is no longer valid causes the rule to be removed. - recvNewNeigh(myIfindex, neighA, NUD_FAILED, macA); - verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macA)); + // NUD_FAILED events do not have a MAC address. + recvNewNeigh(myIfindex, neighA, NUD_FAILED, null); + verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macNull)); reset(mNetd); // A neighbor that is deleted causes the rule to be removed. recvDelNeigh(myIfindex, neighB, NUD_STALE, macB); - verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB)); + verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macNull)); reset(mNetd); // Upstream changes result in deleting and re-adding the rules. From 8fae1ba0570587b887b3f5d36fcb6c9cc5ab10e2 Mon Sep 17 00:00:00 2001 From: Paul Hu Date: Tue, 14 Apr 2020 10:07:54 +0000 Subject: [PATCH 095/188] Update tethering notification strings Update strings after converged with carrier. Bug: 145629001 Bug: 147818698 Test: atests TetheringTests Change-Id: I8ad34e8c93ba1547aa397a8e9c0ecc15286a2b0b Merged-In: I8ad34e8c93ba1547aa397a8e9c0ecc15286a2b0b (cherry picked from commit d4a1bd7b73991c4197b49a3ff36e7683273ae736, aosp/1284584) --- Tethering/res/values-mcc204-mnc04/strings.xml | 16 ++++++++-------- Tethering/res/values-mcc310-mnc004/strings.xml | 16 ++++++++-------- Tethering/res/values-mcc311-mnc480/strings.xml | 16 ++++++++-------- Tethering/res/values/strings.xml | 8 ++++---- 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/Tethering/res/values-mcc204-mnc04/strings.xml b/Tethering/res/values-mcc204-mnc04/strings.xml index a996b4247a..9dadd49cf8 100644 --- a/Tethering/res/values-mcc204-mnc04/strings.xml +++ b/Tethering/res/values-mcc204-mnc04/strings.xml @@ -15,16 +15,16 @@ --> - Hotspot has no internet + Tethering has no internet - Devices can\u2019t connect to internet - - Turn off hotspot + Devices can\u2019t connect + + Turn off tethering - - Hotspot is on - + + Hotspot or tethering is on + Additional charges may apply while roaming - + Continue diff --git a/Tethering/res/values-mcc310-mnc004/strings.xml b/Tethering/res/values-mcc310-mnc004/strings.xml index a996b4247a..9dadd49cf8 100644 --- a/Tethering/res/values-mcc310-mnc004/strings.xml +++ b/Tethering/res/values-mcc310-mnc004/strings.xml @@ -15,16 +15,16 @@ --> - Hotspot has no internet + Tethering has no internet - Devices can\u2019t connect to internet - - Turn off hotspot + Devices can\u2019t connect + + Turn off tethering - - Hotspot is on - + + Hotspot or tethering is on + Additional charges may apply while roaming - + Continue diff --git a/Tethering/res/values-mcc311-mnc480/strings.xml b/Tethering/res/values-mcc311-mnc480/strings.xml index a996b4247a..9dadd49cf8 100644 --- a/Tethering/res/values-mcc311-mnc480/strings.xml +++ b/Tethering/res/values-mcc311-mnc480/strings.xml @@ -15,16 +15,16 @@ --> - Hotspot has no internet + Tethering has no internet - Devices can\u2019t connect to internet - - Turn off hotspot + Devices can\u2019t connect + + Turn off tethering - - Hotspot is on - + + Hotspot or tethering is on + Additional charges may apply while roaming - + Continue diff --git a/Tethering/res/values/strings.xml b/Tethering/res/values/strings.xml index 52a16545c2..4fa60d4125 100644 --- a/Tethering/res/values/strings.xml +++ b/Tethering/res/values/strings.xml @@ -40,13 +40,13 @@ - + - + - + - + From 1acfb0125c563f4e164a6aa46cdc8e0fef6d4b91 Mon Sep 17 00:00:00 2001 From: markchien Date: Tue, 14 Apr 2020 20:19:38 +0800 Subject: [PATCH 096/188] Change tethering file structure to respect its package name Bug: 145099347 Test: atest TetheringTests atest CtsTetheringTest Change-Id: I544ab9480bbaa5e18e030d21a28ab4c4a7265795 --- Tethering/AndroidManifest.xml | 2 +- Tethering/AndroidManifest_InProcess.xml | 2 +- Tethering/proguard.flags | 2 +- Tethering/res/values/config.xml | 2 +- .../tethering/ConnectedClientsTracker.java | 2 +- .../tethering/EntitlementManager.java | 5 ++--- .../tethering/IPv6TetheringCoordinator.java | 2 +- .../tethering/OffloadController.java | 4 ++-- .../tethering/OffloadHardwareInterface.java | 2 +- .../tethering/Tethering.java | 4 ++-- .../tethering/TetheringConfiguration.java | 3 +-- .../tethering/TetheringDependencies.java | 2 +- .../tethering/TetheringInterfaceUtils.java | 2 +- .../tethering/TetheringNotificationUpdater.java | 3 +-- .../tethering/TetheringService.java | 2 +- .../tethering/UpstreamNetworkMonitor.java | 2 +- .../tethering/UpstreamNetworkState.java | 2 +- Tethering/tests/unit/AndroidManifest.xml | 4 ++-- .../tethering/ConnectedClientsTrackerTest.kt | 4 ++-- .../tethering/EntitlementManagerTest.java | 3 +-- .../tethering/IPv6TetheringCoordinatorTest.java | 2 +- .../tethering/MockTetheringService.java | 2 +- .../tethering/OffloadControllerTest.java | 8 ++++---- .../tethering/TetheringConfigurationTest.java | 3 +-- .../tethering/TetheringNotificationUpdaterTest.kt | 7 +++---- .../tethering/TetheringServiceTest.java | 4 ++-- .../tethering/TetheringTest.java | 5 ++--- .../tethering/UpstreamNetworkMonitorTest.java | 4 ++-- 28 files changed, 41 insertions(+), 48 deletions(-) rename Tethering/src/com/android/{server/connectivity => networkstack}/tethering/ConnectedClientsTracker.java (99%) rename Tethering/src/com/android/{server/connectivity => networkstack}/tethering/EntitlementManager.java (99%) rename Tethering/src/com/android/{server/connectivity => networkstack}/tethering/IPv6TetheringCoordinator.java (99%) rename Tethering/src/com/android/{server/connectivity => networkstack}/tethering/OffloadController.java (99%) rename Tethering/src/com/android/{server/connectivity => networkstack}/tethering/OffloadHardwareInterface.java (99%) rename Tethering/src/com/android/{server/connectivity => networkstack}/tethering/Tethering.java (99%) rename Tethering/src/com/android/{server/connectivity => networkstack}/tethering/TetheringConfiguration.java (99%) rename Tethering/src/com/android/{server/connectivity => networkstack}/tethering/TetheringDependencies.java (98%) rename Tethering/src/com/android/{server/connectivity => networkstack}/tethering/TetheringInterfaceUtils.java (98%) rename Tethering/src/com/android/{server/connectivity => networkstack}/tethering/TetheringNotificationUpdater.java (99%) rename Tethering/src/com/android/{server/connectivity => networkstack}/tethering/TetheringService.java (99%) rename Tethering/src/com/android/{server/connectivity => networkstack}/tethering/UpstreamNetworkMonitor.java (99%) rename Tethering/src/com/android/{server/connectivity => networkstack}/tethering/UpstreamNetworkState.java (97%) rename Tethering/tests/unit/src/com/android/{server/connectivity => networkstack}/tethering/ConnectedClientsTrackerTest.kt (99%) rename Tethering/tests/unit/src/com/android/{server/connectivity => networkstack}/tethering/EntitlementManagerTest.java (99%) rename Tethering/tests/unit/src/com/android/{server/connectivity => networkstack}/tethering/IPv6TetheringCoordinatorTest.java (99%) rename Tethering/tests/unit/src/com/android/{server/connectivity => networkstack}/tethering/MockTetheringService.java (96%) rename Tethering/tests/unit/src/com/android/{server/connectivity => networkstack}/tethering/OffloadControllerTest.java (99%) rename Tethering/tests/unit/src/com/android/{server/connectivity => networkstack}/tethering/TetheringConfigurationTest.java (99%) rename Tethering/tests/unit/src/com/android/{server/connectivity => networkstack}/tethering/TetheringNotificationUpdaterTest.kt (98%) rename Tethering/tests/unit/src/com/android/{server/connectivity => networkstack}/tethering/TetheringServiceTest.java (98%) rename Tethering/tests/unit/src/com/android/{server/connectivity => networkstack}/tethering/TetheringTest.java (99%) rename Tethering/tests/unit/src/com/android/{server/connectivity => networkstack}/tethering/UpstreamNetworkMonitorTest.java (99%) diff --git a/Tethering/AndroidManifest.xml b/Tethering/AndroidManifest.xml index ab257475ca..1dc8227e81 100644 --- a/Tethering/AndroidManifest.xml +++ b/Tethering/AndroidManifest.xml @@ -43,7 +43,7 @@ android:process="com.android.networkstack.process" android:extractNativeLibs="false" android:persistent="true"> - diff --git a/Tethering/AndroidManifest_InProcess.xml b/Tethering/AndroidManifest_InProcess.xml index bf1f001e03..b1f124097c 100644 --- a/Tethering/AndroidManifest_InProcess.xml +++ b/Tethering/AndroidManifest_InProcess.xml @@ -22,7 +22,7 @@ android:process="system"> - diff --git a/Tethering/proguard.flags b/Tethering/proguard.flags index 1f83a66382..051fbd19fc 100644 --- a/Tethering/proguard.flags +++ b/Tethering/proguard.flags @@ -1,5 +1,5 @@ # Keep class's integer static field for MessageUtils to parsing their name. --keep class com.android.server.connectivity.tethering.Tethering$TetherMasterSM { +-keep class com.android.networkstack.tethering.Tethering$TetherMasterSM { static final int CMD_*; static final int EVENT_*; } diff --git a/Tethering/res/values/config.xml b/Tethering/res/values/config.xml index 04d6215dce..430fdc4228 100644 --- a/Tethering/res/values/config.xml +++ b/Tethering/res/values/config.xml @@ -89,7 +89,7 @@ TYPE_MOBILE_HIPRI is appended. For other changes applied to this list, now and in the future, see - com.android.server.connectivity.tethering.TetheringConfiguration. + com.android.networkstack.tethering.TetheringConfiguration. Note also: the order of this is important. The first upstream type for which a satisfying network exists is used. diff --git a/Tethering/src/com/android/server/connectivity/tethering/ConnectedClientsTracker.java b/Tethering/src/com/android/networkstack/tethering/ConnectedClientsTracker.java similarity index 99% rename from Tethering/src/com/android/server/connectivity/tethering/ConnectedClientsTracker.java rename to Tethering/src/com/android/networkstack/tethering/ConnectedClientsTracker.java index cdd1a5d978..8a96988ae1 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/ConnectedClientsTracker.java +++ b/Tethering/src/com/android/networkstack/tethering/ConnectedClientsTracker.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import static android.net.TetheringManager.TETHERING_WIFI; diff --git a/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java b/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java similarity index 99% rename from Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java rename to Tethering/src/com/android/networkstack/tethering/EntitlementManager.java index 639cf65d79..4c7b2d49ee 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java +++ b/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import static android.net.TetheringConstants.EXTRA_ADD_TETHER_TYPE; import static android.net.TetheringConstants.EXTRA_PROVISION_CALLBACK; @@ -52,7 +52,6 @@ import android.util.SparseIntArray; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.StateMachine; -import com.android.networkstack.tethering.R; import java.io.PrintWriter; @@ -71,7 +70,7 @@ public class EntitlementManager { @VisibleForTesting protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning"; private static final String ACTION_PROVISIONING_ALARM = - "com.android.server.connectivity.tethering.PROVISIONING_RECHECK_ALARM"; + "com.android.networkstack.tethering.PROVISIONING_RECHECK_ALARM"; private static final String EXTRA_SUBID = "subId"; private final ComponentName mSilentProvisioningService; diff --git a/Tethering/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java b/Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java similarity index 99% rename from Tethering/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java rename to Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java index 66b9ade810..d450c46de7 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java +++ b/Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import android.net.IpPrefix; import android.net.LinkAddress; diff --git a/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java b/Tethering/src/com/android/networkstack/tethering/OffloadController.java similarity index 99% rename from Tethering/src/com/android/server/connectivity/tethering/OffloadController.java rename to Tethering/src/com/android/networkstack/tethering/OffloadController.java index 15cdb6ad7a..c007c174fe 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java +++ b/Tethering/src/com/android/networkstack/tethering/OffloadController.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import static android.net.NetworkStats.DEFAULT_NETWORK_NO; import static android.net.NetworkStats.METERED_NO; @@ -50,7 +50,7 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IndentingPrintWriter; -import com.android.server.connectivity.tethering.OffloadHardwareInterface.ForwardedStats; +import com.android.networkstack.tethering.OffloadHardwareInterface.ForwardedStats; import java.net.Inet4Address; import java.net.Inet6Address; diff --git a/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java b/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java similarity index 99% rename from Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java rename to Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java index b545717208..85a23fb83f 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java +++ b/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import static android.net.util.TetheringUtils.uint16; diff --git a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java similarity index 99% rename from Tethering/src/com/android/server/connectivity/tethering/Tethering.java rename to Tethering/src/com/android/networkstack/tethering/Tethering.java index 4b2c9215f7..f3cead92be 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java +++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import static android.Manifest.permission.NETWORK_SETTINGS; import static android.Manifest.permission.NETWORK_STACK; @@ -60,7 +60,7 @@ import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED; import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; -import static com.android.server.connectivity.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE; +import static com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE; import android.app.usage.NetworkStatsManager; import android.bluetooth.BluetoothAdapter; diff --git a/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java similarity index 99% rename from Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java rename to Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java index 7e9e26f5af..aeac437e24 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import static android.content.Context.TELEPHONY_SERVICE; import static android.net.ConnectivityManager.TYPE_ETHERNET; @@ -33,7 +33,6 @@ import android.telephony.TelephonyManager; import android.text.TextUtils; import com.android.internal.annotations.VisibleForTesting; -import com.android.networkstack.tethering.R; import java.io.PrintWriter; import java.util.ArrayList; diff --git a/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java b/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java similarity index 98% rename from Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java rename to Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java index 0330dad6a1..893c5823dc 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import android.bluetooth.BluetoothAdapter; import android.content.Context; diff --git a/Tethering/src/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java b/Tethering/src/com/android/networkstack/tethering/TetheringInterfaceUtils.java similarity index 98% rename from Tethering/src/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java rename to Tethering/src/com/android/networkstack/tethering/TetheringInterfaceUtils.java index 4dd68301f9..ff38f717a1 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringInterfaceUtils.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import android.annotation.Nullable; import android.net.LinkProperties; diff --git a/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java b/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java similarity index 99% rename from Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java rename to Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java index 992cdd8de6..42870560cb 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import static android.net.TetheringManager.TETHERING_BLUETOOTH; import static android.net.TetheringManager.TETHERING_USB; @@ -41,7 +41,6 @@ import androidx.annotation.IntRange; import androidx.annotation.NonNull; import com.android.internal.annotations.VisibleForTesting; -import com.android.networkstack.tethering.R; /** * A class to display tethering-related notifications. diff --git a/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java b/Tethering/src/com/android/networkstack/tethering/TetheringService.java similarity index 99% rename from Tethering/src/com/android/server/connectivity/tethering/TetheringService.java rename to Tethering/src/com/android/networkstack/tethering/TetheringService.java index c30be25dbd..3ed211520d 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringService.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.net.TetheringManager.TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION; diff --git a/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java similarity index 99% rename from Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java rename to Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java index 45bb4ab6e5..25ddce4404 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java +++ b/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import static android.net.ConnectivityManager.TYPE_BLUETOOTH; import static android.net.ConnectivityManager.TYPE_ETHERNET; diff --git a/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkState.java b/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkState.java similarity index 97% rename from Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkState.java rename to Tethering/src/com/android/networkstack/tethering/UpstreamNetworkState.java index 68bb837593..bab9f84cf7 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkState.java +++ b/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkState.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import android.net.LinkProperties; import android.net.Network; diff --git a/Tethering/tests/unit/AndroidManifest.xml b/Tethering/tests/unit/AndroidManifest.xml index 4ff1d3777f..55640db693 100644 --- a/Tethering/tests/unit/AndroidManifest.xml +++ b/Tethering/tests/unit/AndroidManifest.xml @@ -21,11 +21,11 @@ - + diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/ConnectedClientsTrackerTest.kt b/Tethering/tests/unit/src/com/android/networkstack/tethering/ConnectedClientsTrackerTest.kt similarity index 99% rename from Tethering/tests/unit/src/com/android/server/connectivity/tethering/ConnectedClientsTrackerTest.kt rename to Tethering/tests/unit/src/com/android/networkstack/tethering/ConnectedClientsTrackerTest.kt index 1cdc3bbb99..d915354b0c 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/ConnectedClientsTrackerTest.kt +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/ConnectedClientsTrackerTest.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering +package com.android.networkstack.tethering import android.net.LinkAddress import android.net.MacAddress @@ -159,4 +159,4 @@ class ConnectedClientsTrackerTest { return time } } -} \ No newline at end of file +} diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java similarity index 99% rename from Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java rename to Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java index 6695eed0ff..8bd0edc249 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import static android.net.TetheringManager.TETHERING_BLUETOOTH; import static android.net.TetheringManager.TETHERING_ETHERNET; @@ -59,7 +59,6 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.State; import com.android.internal.util.StateMachine; import com.android.internal.util.test.BroadcastInterceptingContext; -import com.android.networkstack.tethering.R; import org.junit.After; import org.junit.Before; diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinatorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/IPv6TetheringCoordinatorTest.java similarity index 99% rename from Tethering/tests/unit/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinatorTest.java rename to Tethering/tests/unit/src/com/android/networkstack/tethering/IPv6TetheringCoordinatorTest.java index 912124357c..820f255145 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinatorTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/IPv6TetheringCoordinatorTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.RouteInfo.RTN_UNICAST; diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/MockTetheringService.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java similarity index 96% rename from Tethering/tests/unit/src/com/android/server/connectivity/tethering/MockTetheringService.java rename to Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java index 355ece9a44..1c81c1247d 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/MockTetheringService.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import static org.mockito.Mockito.mock; diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java similarity index 99% rename from Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java rename to Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java index fe840864fb..65797200fa 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import static android.net.NetworkStats.DEFAULT_NETWORK_NO; import static android.net.NetworkStats.METERED_NO; @@ -26,9 +26,9 @@ import static android.net.NetworkStats.UID_TETHERING; import static android.net.RouteInfo.RTN_UNICAST; import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED; -import static com.android.server.connectivity.tethering.OffloadController.StatsType.STATS_PER_IFACE; -import static com.android.server.connectivity.tethering.OffloadController.StatsType.STATS_PER_UID; -import static com.android.server.connectivity.tethering.OffloadHardwareInterface.ForwardedStats; +import static com.android.networkstack.tethering.OffloadController.StatsType.STATS_PER_IFACE; +import static com.android.networkstack.tethering.OffloadController.StatsType.STATS_PER_UID; +import static com.android.networkstack.tethering.OffloadHardwareInterface.ForwardedStats; import static com.android.testutils.MiscAssertsKt.assertContainsAll; import static com.android.testutils.MiscAssertsKt.assertThrows; import static com.android.testutils.NetworkStatsUtilsKt.orderInsensitiveEquals; diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java similarity index 99% rename from Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java rename to Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java index 3635964dd6..07ddea43f4 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import static android.net.ConnectivityManager.TYPE_ETHERNET; import static android.net.ConnectivityManager.TYPE_MOBILE; @@ -44,7 +44,6 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.test.BroadcastInterceptingContext; -import com.android.networkstack.tethering.R; import org.junit.After; import org.junit.Before; diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringNotificationUpdaterTest.kt b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt similarity index 98% rename from Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringNotificationUpdaterTest.kt rename to Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt index b86949185c..7bff74b25d 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringNotificationUpdaterTest.kt +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering +package com.android.networkstack.tethering import android.app.Notification import android.app.NotificationManager @@ -29,8 +29,7 @@ import androidx.test.platform.app.InstrumentationRegistry import androidx.test.filters.SmallTest import androidx.test.runner.AndroidJUnit4 import com.android.internal.util.test.BroadcastInterceptingContext -import com.android.networkstack.tethering.R -import com.android.server.connectivity.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE +import com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test @@ -259,4 +258,4 @@ class TetheringNotificationUpdaterTest { notificationUpdater.notifyTetheringDisabledByRestriction() verifyNotification(R.drawable.stat_sys_tether_general, disallowTitle, disallowMessage) } -} \ No newline at end of file +} diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringServiceTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java similarity index 98% rename from Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringServiceTest.java rename to Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java index d9d3e73eb4..51bad9af23 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringServiceTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import static android.net.TetheringManager.TETHERING_WIFI; import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; @@ -37,7 +37,7 @@ import androidx.test.filters.SmallTest; import androidx.test.rule.ServiceTestRule; import androidx.test.runner.AndroidJUnit4; -import com.android.server.connectivity.tethering.MockTetheringService.MockTetheringConnector; +import com.android.networkstack.tethering.MockTetheringService.MockTetheringConnector; import org.junit.After; import org.junit.Before; diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java similarity index 99% rename from Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java rename to Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java index 2955903c84..d4be3a26d9 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import static android.hardware.usb.UsbManager.USB_CONFIGURED; import static android.hardware.usb.UsbManager.USB_CONNECTED; @@ -47,7 +47,7 @@ import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED; import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; -import static com.android.server.connectivity.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE; +import static com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -137,7 +137,6 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.StateMachine; import com.android.internal.util.test.BroadcastInterceptingContext; import com.android.internal.util.test.FakeSettingsProvider; -import com.android.networkstack.tethering.R; import com.android.testutils.MiscAssertsKt; import org.junit.After; diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java similarity index 99% rename from Tethering/tests/unit/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java rename to Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java index 7c98f626a4..232588c7ee 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering; +package com.android.networkstack.tethering; import static android.net.ConnectivityManager.TYPE_MOBILE_DUN; import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI; @@ -24,7 +24,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; -import static com.android.server.connectivity.tethering.UpstreamNetworkMonitor.TYPE_NONE; +import static com.android.networkstack.tethering.UpstreamNetworkMonitor.TYPE_NONE; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; From d1ebb1e3485c81c8deb9060d7d77fbf5de869484 Mon Sep 17 00:00:00 2001 From: paulhu Date: Mon, 6 Apr 2020 14:58:12 +0800 Subject: [PATCH 097/188] Add TetheringCoverageTests Bug: 148636687 Test: atest TetheringCoverageTests atest TetheringTests atest TetheringIntegrationTests atest NetworkStackTests atest CtsTetheringTest Change-Id: I1f2a50f16894b05e988476520ba25baba0b60d88 --- Tethering/tests/integration/Android.bp | 56 ++++++++++++++++--- .../tests/integration/AndroidManifest.xml | 1 - .../integration/AndroidManifest_coverage.xml | 29 ++++++++++ .../integration/AndroidTest_Coverage.xml | 12 ++++ .../android/net/EthernetTetheringTest.java | 4 +- Tethering/tests/unit/Android.bp | 46 ++++++++++----- 6 files changed, 122 insertions(+), 26 deletions(-) create mode 100644 Tethering/tests/integration/AndroidManifest_coverage.xml create mode 100644 Tethering/tests/integration/AndroidTest_Coverage.xml diff --git a/Tethering/tests/integration/Android.bp b/Tethering/tests/integration/Android.bp index 620261b375..6b751afdf5 100644 --- a/Tethering/tests/integration/Android.bp +++ b/Tethering/tests/integration/Android.bp @@ -13,19 +13,12 @@ // See the License for the specific language governing permissions and // limitations under the License. // - -android_test { - name: "TetheringIntegrationTests", - certificate: "platform", - platform_apis: true, +java_defaults { + name: "TetheringIntegrationTestsDefaults", srcs: [ "src/**/*.java", "src/**/*.kt", ], - test_suites: [ - "device-tests", - "mts", - ], static_libs: [ "NetworkStackApiStableLib", "androidx.test.rules", @@ -44,4 +37,49 @@ android_test { "libdexmakerjvmtiagent", "libstaticjvmtiagent", ], + jarjar_rules: ":NetworkStackJarJarRules", } + +android_library { + name: "TetheringIntegrationTestsLib", + platform_apis: true, + defaults: ["TetheringIntegrationTestsDefaults"], + visibility: ["//cts/tests/tests/tethering"] +} + +android_test { + name: "TetheringIntegrationTests", + platform_apis: true, + defaults: ["TetheringIntegrationTestsDefaults"], + test_suites: [ + "device-tests", + "mts", + ], + compile_multilib: "both", +} + +// Special version of the tethering tests that includes all tests necessary for code coverage +// purposes. This is currently the union of TetheringTests, TetheringIntegrationTests and +// NetworkStackTests. +android_test { + name: "TetheringCoverageTests", + certificate: "platform", + platform_apis: true, + test_suites: ["device-tests", "mts"], + test_config: "AndroidTest_Coverage.xml", + defaults: ["libnetworkstackutilsjni_deps"], + static_libs: [ + "NetworkStackTestsLib", + "TetheringTestsLib", + "TetheringIntegrationTestsLib", + ], + jni_libs: [ + // For mockito extended + "libdexmakerjvmtiagent", + "libstaticjvmtiagent", + // For NetworkStackUtils included in NetworkStackBase + "libnetworkstackutilsjni", + ], + compile_multilib: "both", + manifest: "AndroidManifest_coverage.xml", +} \ No newline at end of file diff --git a/Tethering/tests/integration/AndroidManifest.xml b/Tethering/tests/integration/AndroidManifest.xml index 233ba40b5d..fddfaad29f 100644 --- a/Tethering/tests/integration/AndroidManifest.xml +++ b/Tethering/tests/integration/AndroidManifest.xml @@ -17,7 +17,6 @@ package="com.android.networkstack.tethering.tests.integration"> - diff --git a/Tethering/tests/integration/AndroidManifest_coverage.xml b/Tethering/tests/integration/AndroidManifest_coverage.xml new file mode 100644 index 0000000000..06de00d785 --- /dev/null +++ b/Tethering/tests/integration/AndroidManifest_coverage.xml @@ -0,0 +1,29 @@ + + + + + + + + + + diff --git a/Tethering/tests/integration/AndroidTest_Coverage.xml b/Tethering/tests/integration/AndroidTest_Coverage.xml new file mode 100644 index 0000000000..3def2099e4 --- /dev/null +++ b/Tethering/tests/integration/AndroidTest_Coverage.xml @@ -0,0 +1,12 @@ + + + + + \ No newline at end of file diff --git a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java index b02bb23f98..4bac9da9c3 100644 --- a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java +++ b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java @@ -18,6 +18,7 @@ package android.net; import static android.Manifest.permission.MANAGE_TEST_NETWORKS; import static android.Manifest.permission.NETWORK_SETTINGS; +import static android.Manifest.permission.TETHER_PRIVILEGED; import static android.net.TetheringManager.TETHERING_ETHERNET; import static org.junit.Assert.assertEquals; @@ -109,7 +110,8 @@ public class EthernetTetheringTest { mTetheredInterfaceRequester = new TetheredInterfaceRequester(mHandler, mEm); // Needed to create a TestNetworkInterface, to call requestTetheredInterface, and to receive // tethered client callbacks. - mUiAutomation.adoptShellPermissionIdentity(MANAGE_TEST_NETWORKS, NETWORK_SETTINGS); + mUiAutomation.adoptShellPermissionIdentity( + MANAGE_TEST_NETWORKS, NETWORK_SETTINGS, TETHER_PRIVILEGED); } private void cleanUp() throws Exception { diff --git a/Tethering/tests/unit/Android.bp b/Tethering/tests/unit/Android.bp index 59681e9eb5..4849fd5d01 100644 --- a/Tethering/tests/unit/Android.bp +++ b/Tethering/tests/unit/Android.bp @@ -14,39 +14,33 @@ // limitations under the License. // -android_test { - name: "TetheringTests", - certificate: "platform", +java_defaults { + name: "TetheringTestsDefaults", srcs: [ "src/**/*.java", "src/**/*.kt", ], - test_suites: [ - "device-tests", - "mts", - ], - compile_multilib: "both", static_libs: [ + "TetheringApiCurrentLib", "androidx.test.rules", "frameworks-base-testutils", - "net-tests-utils", "mockito-target-extended-minus-junit4", - "TetheringApiCurrentLib", + "net-tests-utils", "testables", ], // TODO(b/147200698) change sdk_version to module-current and // remove framework-minus-apex, ext, and framework-res sdk_version: "core_platform", libs: [ - "framework-minus-apex", - "ext", - "framework-res", - "framework-wifi-stubs-module_libs_api", - "framework-telephony-stubs", "android.test.runner", "android.test.base", "android.test.mock", + "ext", + "framework-minus-apex", + "framework-res", + "framework-telephony-stubs", "framework-tethering", + "framework-wifi-stubs-module_libs_api", ], jni_libs: [ // For mockito extended @@ -55,3 +49,25 @@ android_test { ], jarjar_rules: "jarjar-rules.txt", } + +// Library containing the unit tests. This is used by the coverage test target to pull in the +// unit test code. It is not currently used by the tests themselves because all the build +// configuration needed by the tests is in the TetheringTestsDefaults rule. +android_library { + name: "TetheringTestsLib", + defaults: ["TetheringTestsDefaults"], + visibility: [ + "//frameworks/base/packages/Tethering/tests/integration", + ] +} + +android_test { + name: "TetheringTests", + certificate: "platform", + test_suites: [ + "device-tests", + "mts", + ], + defaults: ["TetheringTestsDefaults"], + compile_multilib: "both", +} From 69f3fa3729e399670c6ba53150fc56bdd99ac363 Mon Sep 17 00:00:00 2001 From: Amit Mahajan Date: Tue, 7 Apr 2020 07:34:45 -0700 Subject: [PATCH 098/188] DO NOT MERGE Remove references of telephony-stubs. Since it's not used for now. Test: TH Bug: 153304048 Change-Id: I1812818c3d49463c3840a98212bbab58a110359a --- Tethering/Android.bp | 1 - Tethering/tests/unit/Android.bp | 1 - 2 files changed, 2 deletions(-) diff --git a/Tethering/Android.bp b/Tethering/Android.bp index 5b052df75e..bfb65241ec 100644 --- a/Tethering/Android.bp +++ b/Tethering/Android.bp @@ -34,7 +34,6 @@ java_defaults { ], libs: [ "framework-tethering", - "framework-telephony-stubs", "framework-wifi-stubs-systemapi", "unsupportedappusage", ], diff --git a/Tethering/tests/unit/Android.bp b/Tethering/tests/unit/Android.bp index 4849fd5d01..26517ceb72 100644 --- a/Tethering/tests/unit/Android.bp +++ b/Tethering/tests/unit/Android.bp @@ -38,7 +38,6 @@ java_defaults { "ext", "framework-minus-apex", "framework-res", - "framework-telephony-stubs", "framework-tethering", "framework-wifi-stubs-module_libs_api", ], From ee27d55295a2548947b0fa98841b4684f286dcfd Mon Sep 17 00:00:00 2001 From: Amit Mahajan Date: Tue, 7 Apr 2020 07:34:45 -0700 Subject: [PATCH 099/188] DO NOT MERGE Remove references of telephony-stubs. Since it's not used for now. Test: TH Bug: 153304048 Merged-in: I1812818c3d49463c3840a98212bbab58a110359a Change-Id: I1812818c3d49463c3840a98212bbab58a110359a --- Tethering/Android.bp | 1 - Tethering/tests/unit/Android.bp | 1 - 2 files changed, 2 deletions(-) diff --git a/Tethering/Android.bp b/Tethering/Android.bp index 5b052df75e..bfb65241ec 100644 --- a/Tethering/Android.bp +++ b/Tethering/Android.bp @@ -34,7 +34,6 @@ java_defaults { ], libs: [ "framework-tethering", - "framework-telephony-stubs", "framework-wifi-stubs-systemapi", "unsupportedappusage", ], diff --git a/Tethering/tests/unit/Android.bp b/Tethering/tests/unit/Android.bp index 4849fd5d01..26517ceb72 100644 --- a/Tethering/tests/unit/Android.bp +++ b/Tethering/tests/unit/Android.bp @@ -38,7 +38,6 @@ java_defaults { "ext", "framework-minus-apex", "framework-res", - "framework-telephony-stubs", "framework-tethering", "framework-wifi-stubs-module_libs_api", ], From 8c98b3d35e187b476f9669b183c957ddd738fdfd Mon Sep 17 00:00:00 2001 From: Mark Chien Date: Wed, 15 Apr 2020 17:28:27 +0000 Subject: [PATCH 100/188] Do not stop IpServer when recieve wifi ap disabling state. This is used to fix the race when quickly OFF/ON wifi tethering. When IpServer is started/stopped, there is callback update tethering interface status. Before this change, IpServer is stopped when wifi ap is disabling. Then the next startTethering may fail in wifi because wifi is in disabling state. Error pattern: WifiService: Tethering is already active. No unitest for this CL but it fixed the CtsTetheringTest flakty rate than around 30% to 0 for more than 100 runs. Bug: 153925821 Test: atest CtsTetheringTest --iteration 100 Merged-In: I8b65f621abe20799a3a0d410ba1f06368746ee49 Change-Id: I8b65f621abe20799a3a0d410ba1f06368746ee49 --- .../src/com/android/networkstack/tethering/Tethering.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java index f3cead92be..bae54a5c76 100644 --- a/Tethering/src/com/android/networkstack/tethering/Tethering.java +++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java @@ -922,8 +922,10 @@ public class Tethering { case WifiManager.WIFI_AP_STATE_ENABLED: enableWifiIpServingLocked(ifname, ipmode); break; - case WifiManager.WIFI_AP_STATE_DISABLED: case WifiManager.WIFI_AP_STATE_DISABLING: + // We can see this state on the way to disabled. + break; + case WifiManager.WIFI_AP_STATE_DISABLED: case WifiManager.WIFI_AP_STATE_FAILED: default: disableWifiIpServingLocked(ifname, curState); From af6a2a33f5b0f59fdf16884db04f09e587c3b0bf Mon Sep 17 00:00:00 2001 From: Paul Hu Date: Thu, 16 Apr 2020 02:54:37 +0000 Subject: [PATCH 101/188] [TNU05] Add no upstream notification Reminder user of unavailable tethering status if there is no internet access. Bug: 147818698 Test: atest TetheringTests Change-Id: Ic6557f9f7703337596100cd6a477fd7239217166 Merged-In: Ic6557f9f7703337596100cd6a477fd7239217166 (cherry picked from commit ac1b098acc504b60e85b3dcd22941f4e293865ae, aosp/1237036) --- Tethering/AndroidManifest.xml | 3 + Tethering/res/values-mcc204-mnc04/strings.xml | 30 --- Tethering/res/values-mcc310-mnc004/config.xml | 20 ++ Tethering/res/values-mcc311-mnc480/config.xml | 20 ++ Tethering/res/values/config.xml | 6 + .../networkstack/tethering/Tethering.java | 10 +- .../tethering/TetheringDependencies.java | 5 +- .../TetheringNotificationUpdater.java | 185 ++++++++++++-- Tethering/tests/unit/AndroidManifest.xml | 1 + .../TetheringNotificationUpdaterTest.kt | 239 +++++++++++++++--- .../networkstack/tethering/TetheringTest.java | 14 +- 11 files changed, 446 insertions(+), 87 deletions(-) delete mode 100644 Tethering/res/values-mcc204-mnc04/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004/config.xml create mode 100644 Tethering/res/values-mcc311-mnc480/config.xml diff --git a/Tethering/AndroidManifest.xml b/Tethering/AndroidManifest.xml index 1dc8227e81..2b2fe4534c 100644 --- a/Tethering/AndroidManifest.xml +++ b/Tethering/AndroidManifest.xml @@ -34,11 +34,14 @@ + + + - - - - Tethering has no internet - - Devices can\u2019t connect - - Turn off tethering - - - Hotspot or tethering is on - - Additional charges may apply while roaming - - Continue - diff --git a/Tethering/res/values-mcc310-mnc004/config.xml b/Tethering/res/values-mcc310-mnc004/config.xml new file mode 100644 index 0000000000..8c627d5df0 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004/config.xml @@ -0,0 +1,20 @@ + + + + + 5000 + \ No newline at end of file diff --git a/Tethering/res/values-mcc311-mnc480/config.xml b/Tethering/res/values-mcc311-mnc480/config.xml new file mode 100644 index 0000000000..8c627d5df0 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480/config.xml @@ -0,0 +1,20 @@ + + + + + 5000 + \ No newline at end of file diff --git a/Tethering/res/values/config.xml b/Tethering/res/values/config.xml index 430fdc4228..52aa5bbaff 100644 --- a/Tethering/res/values/config.xml +++ b/Tethering/res/values/config.xml @@ -202,4 +202,10 @@ @string/tethered_notification_title @string/tethered_notification_message + + + + -1 diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java index bae54a5c76..c19f756439 100644 --- a/Tethering/src/com/android/networkstack/tethering/Tethering.java +++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java @@ -257,7 +257,7 @@ public class Tethering { mContext = mDeps.getContext(); mNetd = mDeps.getINetd(mContext); mLooper = mDeps.getTetheringLooper(); - mNotificationUpdater = mDeps.getNotificationUpdater(mContext); + mNotificationUpdater = mDeps.getNotificationUpdater(mContext, mLooper); mPublicSync = new Object(); @@ -337,6 +337,11 @@ public class Tethering { filter.addAction(ACTION_RESTRICT_BACKGROUND_CHANGED); mContext.registerReceiver(mStateReceiver, filter, null, mHandler); + final IntentFilter noUpstreamFilter = new IntentFilter(); + noUpstreamFilter.addAction(TetheringNotificationUpdater.ACTION_DISABLE_TETHERING); + mContext.registerReceiver( + mStateReceiver, noUpstreamFilter, PERMISSION_MAINLINE_NETWORK_STACK, mHandler); + final WifiManager wifiManager = getWifiManager(); if (wifiManager != null) { wifiManager.registerSoftApCallback(mExecutor, new TetheringSoftApCallback()); @@ -855,6 +860,8 @@ public class Tethering { } else if (action.equals(ACTION_RESTRICT_BACKGROUND_CHANGED)) { mLog.log("OBSERVED data saver changed"); handleDataSaverChanged(); + } else if (action.equals(TetheringNotificationUpdater.ACTION_DISABLE_TETHERING)) { + untetherAll(); } } @@ -2013,6 +2020,7 @@ public class Tethering { } finally { mTetheringEventCallbacks.finishBroadcast(); } + mNotificationUpdater.onUpstreamNetworkChanged(network); } private void reportConfigurationChanged(TetheringConfigurationParcel config) { diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java b/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java index 893c5823dc..9b54b5ff24 100644 --- a/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java @@ -106,8 +106,9 @@ public abstract class TetheringDependencies { /** * Get a reference to the TetheringNotificationUpdater to be used by tethering. */ - public TetheringNotificationUpdater getNotificationUpdater(@NonNull final Context ctx) { - return new TetheringNotificationUpdater(ctx); + public TetheringNotificationUpdater getNotificationUpdater(@NonNull final Context ctx, + @NonNull final Looper looper) { + return new TetheringNotificationUpdater(ctx, looper); } /** diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java b/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java index 42870560cb..ff83fd1e4f 100644 --- a/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java @@ -19,18 +19,25 @@ package com.android.networkstack.tethering; import static android.net.TetheringManager.TETHERING_BLUETOOTH; import static android.net.TetheringManager.TETHERING_USB; import static android.net.TetheringManager.TETHERING_WIFI; +import static android.text.TextUtils.isEmpty; import android.app.Notification; +import android.app.Notification.Action; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; +import android.content.res.Configuration; import android.content.res.Resources; +import android.net.Network; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; import android.os.UserHandle; import android.provider.Settings; import android.telephony.SubscriptionManager; -import android.text.TextUtils; +import android.telephony.TelephonyManager; import android.util.Log; import android.util.SparseArray; @@ -39,9 +46,13 @@ import androidx.annotation.DrawableRes; import androidx.annotation.IntDef; import androidx.annotation.IntRange; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; +import java.util.Arrays; +import java.util.List; + /** * A class to display tethering-related notifications. * @@ -58,12 +69,22 @@ public class TetheringNotificationUpdater { private static final String WIFI_DOWNSTREAM = "WIFI"; private static final String USB_DOWNSTREAM = "USB"; private static final String BLUETOOTH_DOWNSTREAM = "BT"; + @VisibleForTesting + static final String ACTION_DISABLE_TETHERING = + "com.android.server.connectivity.tethering.DISABLE_TETHERING"; private static final boolean NOTIFY_DONE = true; private static final boolean NO_NOTIFY = false; - // Id to update and cancel tethering notification. Must be unique within the tethering app. - private static final int ENABLE_NOTIFICATION_ID = 1000; + @VisibleForTesting + static final int EVENT_SHOW_NO_UPSTREAM = 1; + // Id to update and cancel enable notification. Must be unique within the tethering app. + @VisibleForTesting + static final int ENABLE_NOTIFICATION_ID = 1000; // Id to update and cancel restricted notification. Must be unique within the tethering app. - private static final int RESTRICTED_NOTIFICATION_ID = 1001; + @VisibleForTesting + static final int RESTRICTED_NOTIFICATION_ID = 1001; + // Id to update and cancel no upstream notification. Must be unique within the tethering app. + @VisibleForTesting + static final int NO_UPSTREAM_NOTIFICATION_ID = 1002; @VisibleForTesting static final int NO_ICON_ID = 0; @VisibleForTesting @@ -71,14 +92,16 @@ public class TetheringNotificationUpdater { private final Context mContext; private final NotificationManager mNotificationManager; private final NotificationChannel mChannel; + private final Handler mHandler; // WARNING : the constructor is called on a different thread. Thread safety therefore - // relies on this value being initialized to 0, and not any other value. If you need + // relies on these values being initialized to 0 or false, and not any other value. If you need // to change this, you will need to change the thread where the constructor is invoked, // or to introduce synchronization. // Downstream type is one of ConnectivityManager.TETHERING_* constants, 0 1 or 2. // This value has to be made 1 2 and 4, and OR'd with the others. private int mDownstreamTypesMask = DOWNSTREAM_NONE; + private boolean mNoUpstream = false; // WARNING : this value is not able to being initialized to 0 and must have volatile because // telephony service is not guaranteed that is up before tethering service starts. If telephony @@ -87,10 +110,30 @@ public class TetheringNotificationUpdater { // INVALID_SUBSCRIPTION_ID. private volatile int mActiveDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; - @IntDef({ENABLE_NOTIFICATION_ID, RESTRICTED_NOTIFICATION_ID}) + @IntDef({ENABLE_NOTIFICATION_ID, RESTRICTED_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID}) @interface NotificationId {} - public TetheringNotificationUpdater(@NonNull final Context context) { + private static final class MccMncOverrideInfo { + public final List visitedMccMncs; + public final int homeMcc; + public final int homeMnc; + MccMncOverrideInfo(List visitedMccMncs, int mcc, int mnc) { + this.visitedMccMncs = visitedMccMncs; + this.homeMcc = mcc; + this.homeMnc = mnc; + } + } + + private static final SparseArray sCarrierIdToMccMnc = new SparseArray<>(); + + static { + // VZW + sCarrierIdToMccMnc.put( + 1839, new MccMncOverrideInfo(Arrays.asList(new String[] {"20404"}), 311, 480)); + } + + public TetheringNotificationUpdater(@NonNull final Context context, + @NonNull final Looper looper) { mContext = context; mNotificationManager = (NotificationManager) context.createContextAsUser(UserHandle.ALL, 0) .getSystemService(Context.NOTIFICATION_SERVICE); @@ -99,6 +142,22 @@ public class TetheringNotificationUpdater { context.getResources().getString(R.string.notification_channel_tethering_status), NotificationManager.IMPORTANCE_LOW); mNotificationManager.createNotificationChannel(mChannel); + mHandler = new NotificationHandler(looper); + } + + private class NotificationHandler extends Handler { + NotificationHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + switch(msg.what) { + case EVENT_SHOW_NO_UPSTREAM: + notifyTetheringNoUpstream(); + break; + } + } } /** Called when downstream has changed */ @@ -106,6 +165,7 @@ public class TetheringNotificationUpdater { if (mDownstreamTypesMask == downstreamTypesMask) return; mDownstreamTypesMask = downstreamTypesMask; updateEnableNotification(); + updateNoUpstreamNotification(); } /** Called when active data subscription id changed */ @@ -113,21 +173,62 @@ public class TetheringNotificationUpdater { if (mActiveDataSubId == subId) return; mActiveDataSubId = subId; updateEnableNotification(); + updateNoUpstreamNotification(); } + /** Called when upstream network changed */ + public void onUpstreamNetworkChanged(@Nullable final Network network) { + final boolean isNoUpstream = (network == null); + if (mNoUpstream == isNoUpstream) return; + mNoUpstream = isNoUpstream; + updateNoUpstreamNotification(); + } + + @NonNull @VisibleForTesting - Resources getResourcesForSubId(@NonNull final Context c, final int subId) { - return SubscriptionManager.getResourcesForSubId(c, subId); + final Handler getHandler() { + return mHandler; + } + + @NonNull + @VisibleForTesting + Resources getResourcesForSubId(@NonNull final Context context, final int subId) { + final Resources res = SubscriptionManager.getResourcesForSubId(context, subId); + final TelephonyManager tm = + ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE)) + .createForSubscriptionId(mActiveDataSubId); + final int carrierId = tm.getSimCarrierId(); + final String mccmnc = tm.getSimOperator(); + final MccMncOverrideInfo overrideInfo = sCarrierIdToMccMnc.get(carrierId); + if (overrideInfo != null && overrideInfo.visitedMccMncs.contains(mccmnc)) { + // Re-configure MCC/MNC value to specific carrier to get right resources. + final Configuration config = res.getConfiguration(); + config.mcc = overrideInfo.homeMcc; + config.mnc = overrideInfo.homeMnc; + return context.createConfigurationContext(config).getResources(); + } + return res; } private void updateEnableNotification() { - final boolean tetheringInactive = mDownstreamTypesMask <= DOWNSTREAM_NONE; + final boolean tetheringInactive = mDownstreamTypesMask == DOWNSTREAM_NONE; if (tetheringInactive || setupNotification() == NO_NOTIFY) { clearNotification(ENABLE_NOTIFICATION_ID); } } + private void updateNoUpstreamNotification() { + final boolean tetheringInactive = mDownstreamTypesMask == DOWNSTREAM_NONE; + + if (tetheringInactive + || !mNoUpstream + || setupNoUpstreamNotification() == NO_NOTIFY) { + clearNotification(NO_UPSTREAM_NOTIFICATION_ID); + mHandler.removeMessages(EVENT_SHOW_NO_UPSTREAM); + } + } + @VisibleForTesting void tetheringRestrictionLifted() { clearNotification(RESTRICTED_NOTIFICATION_ID); @@ -142,9 +243,38 @@ public class TetheringNotificationUpdater { final Resources res = getResourcesForSubId(mContext, mActiveDataSubId); final String title = res.getString(R.string.disable_tether_notification_title); final String message = res.getString(R.string.disable_tether_notification_message); + if (isEmpty(title) || isEmpty(message)) return; + + final PendingIntent pi = PendingIntent.getActivity( + mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */), + 0 /* requestCode */, + new Intent(Settings.ACTION_TETHER_SETTINGS), + Intent.FLAG_ACTIVITY_NEW_TASK, + null /* options */); showNotification(R.drawable.stat_sys_tether_general, title, message, - RESTRICTED_NOTIFICATION_ID); + RESTRICTED_NOTIFICATION_ID, pi, new Action[0]); + } + + private void notifyTetheringNoUpstream() { + final Resources res = getResourcesForSubId(mContext, mActiveDataSubId); + final String title = res.getString(R.string.no_upstream_notification_title); + final String message = res.getString(R.string.no_upstream_notification_message); + final String disableButton = + res.getString(R.string.no_upstream_notification_disable_button); + if (isEmpty(title) || isEmpty(message) || isEmpty(disableButton)) return; + + final Intent intent = new Intent(ACTION_DISABLE_TETHERING); + intent.setPackage(mContext.getPackageName()); + final PendingIntent pi = PendingIntent.getBroadcast( + mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */), + 0 /* requestCode */, + intent, + 0 /* flags */); + final Action action = new Action.Builder(NO_ICON_ID, disableButton, pi).build(); + + showNotification(R.drawable.stat_sys_tether_general, title, message, + NO_UPSTREAM_NOTIFICATION_ID, null /* pendingIntent */, action); } /** @@ -179,12 +309,13 @@ public class TetheringNotificationUpdater { * * @return {@link android.util.SparseArray} with downstream types and icon id info. */ + @NonNull @VisibleForTesting SparseArray getIcons(@ArrayRes int id, @NonNull Resources res) { final String[] array = res.getStringArray(id); final SparseArray icons = new SparseArray<>(); for (String config : array) { - if (TextUtils.isEmpty(config)) continue; + if (isEmpty(config)) continue; final String[] elements = config.split(";"); if (elements.length != 2) { @@ -204,6 +335,18 @@ public class TetheringNotificationUpdater { return icons; } + private boolean setupNoUpstreamNotification() { + final Resources res = getResourcesForSubId(mContext, mActiveDataSubId); + final int delayToShowUpstreamNotification = + res.getInteger(R.integer.delay_to_show_no_upstream_after_no_backhaul); + + if (delayToShowUpstreamNotification < 0) return NO_NOTIFY; + + mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_SHOW_NO_UPSTREAM), + delayToShowUpstreamNotification); + return NOTIFY_DONE; + } + private boolean setupNotification() { final Resources res = getResourcesForSubId(mContext, mActiveDataSubId); final SparseArray downstreamIcons = @@ -214,17 +357,22 @@ public class TetheringNotificationUpdater { final String title = res.getString(R.string.tethering_notification_title); final String message = res.getString(R.string.tethering_notification_message); + if (isEmpty(title) || isEmpty(message)) return NO_NOTIFY; - showNotification(iconId, title, message, ENABLE_NOTIFICATION_ID); + final PendingIntent pi = PendingIntent.getActivity( + mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */), + 0 /* requestCode */, + new Intent(Settings.ACTION_TETHER_SETTINGS), + Intent.FLAG_ACTIVITY_NEW_TASK, + null /* options */); + + showNotification(iconId, title, message, ENABLE_NOTIFICATION_ID, pi, new Action[0]); return NOTIFY_DONE; } private void showNotification(@DrawableRes final int iconId, @NonNull final String title, - @NonNull final String message, @NotificationId final int id) { - final Intent intent = new Intent(Settings.ACTION_TETHER_SETTINGS); - final PendingIntent pi = PendingIntent.getActivity( - mContext.createContextAsUser(UserHandle.CURRENT, 0), - 0 /* requestCode */, intent, 0 /* flags */, null /* options */); + @NonNull final String message, @NotificationId final int id, @Nullable PendingIntent pi, + @NonNull final Action... actions) { final Notification notification = new Notification.Builder(mContext, mChannel.getId()) .setSmallIcon(iconId) @@ -236,6 +384,7 @@ public class TetheringNotificationUpdater { .setVisibility(Notification.VISIBILITY_PUBLIC) .setCategory(Notification.CATEGORY_STATUS) .setContentIntent(pi) + .setActions(actions) .build(); mNotificationManager.notify(null /* tag */, id, notification); diff --git a/Tethering/tests/unit/AndroidManifest.xml b/Tethering/tests/unit/AndroidManifest.xml index 55640db693..31eaabff52 100644 --- a/Tethering/tests/unit/AndroidManifest.xml +++ b/Tethering/tests/unit/AndroidManifest.xml @@ -16,6 +16,7 @@ + diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt index 7bff74b25d..5f8858857c 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt @@ -23,14 +23,26 @@ import android.content.res.Resources import android.net.ConnectivityManager.TETHERING_BLUETOOTH import android.net.ConnectivityManager.TETHERING_USB import android.net.ConnectivityManager.TETHERING_WIFI +import android.net.Network +import android.os.Handler +import android.os.HandlerThread +import android.os.Looper import android.os.UserHandle import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID -import androidx.test.platform.app.InstrumentationRegistry +import android.telephony.TelephonyManager import androidx.test.filters.SmallTest +import androidx.test.platform.app.InstrumentationRegistry import androidx.test.runner.AndroidJUnit4 import com.android.internal.util.test.BroadcastInterceptingContext import com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE +import com.android.networkstack.tethering.TetheringNotificationUpdater.ENABLE_NOTIFICATION_ID +import com.android.networkstack.tethering.TetheringNotificationUpdater.EVENT_SHOW_NO_UPSTREAM +import com.android.networkstack.tethering.TetheringNotificationUpdater.NO_UPSTREAM_NOTIFICATION_ID +import com.android.networkstack.tethering.TetheringNotificationUpdater.RESTRICTED_NOTIFICATION_ID +import com.android.testutils.waitForIdle +import org.junit.After import org.junit.Assert.assertEquals +import org.junit.Assert.fail import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -43,8 +55,8 @@ import org.mockito.Mockito.doReturn import org.mockito.Mockito.never import org.mockito.Mockito.reset import org.mockito.Mockito.times -import org.mockito.Mockito.verifyZeroInteractions import org.mockito.Mockito.verify +import org.mockito.Mockito.verifyZeroInteractions import org.mockito.MockitoAnnotations const val TEST_SUBID = 1 @@ -55,10 +67,13 @@ const val GENERAL_ICON_ID = 4 const val WIFI_MASK = 1 shl TETHERING_WIFI const val USB_MASK = 1 shl TETHERING_USB const val BT_MASK = 1 shl TETHERING_BLUETOOTH -const val TITTLE = "Tethering active" +const val TITLE = "Tethering active" const val MESSAGE = "Tap here to set up." -const val TEST_TITTLE = "Hotspot active" +const val TEST_TITLE = "Hotspot active" const val TEST_MESSAGE = "Tap to set up hotspot." +const val TEST_NO_UPSTREAM_TITLE = "Hotspot has no internet access" +const val TEST_NO_UPSTREAM_MESSAGE = "Device cannot connect to internet." +const val TEST_NO_UPSTREAM_BUTTON = "Turn off hotspot" @RunWith(AndroidJUnit4::class) @SmallTest @@ -67,12 +82,15 @@ class TetheringNotificationUpdaterTest { // should crash if they are used before being initialized. @Mock private lateinit var mockContext: Context @Mock private lateinit var notificationManager: NotificationManager + @Mock private lateinit var telephonyManager: TelephonyManager @Mock private lateinit var defaultResources: Resources @Mock private lateinit var testResources: Resources - // lateinit for this class under test, as it should be reset to a different instance for every - // tests but should always be initialized before use (or the test should crash). + // lateinit for these classes under test, as they should be reset to a different instance for + // every test but should always be initialized before use (or the test should crash). + private lateinit var context: TestContext private lateinit var notificationUpdater: TetheringNotificationUpdater + private lateinit var fakeTetheringThread: HandlerThread private val ENABLE_ICON_CONFIGS = arrayOf( "USB;android.test:drawable/usb", "BT;android.test:drawable/bluetooth", @@ -82,11 +100,19 @@ class TetheringNotificationUpdaterTest { private inner class TestContext(c: Context) : BroadcastInterceptingContext(c) { override fun createContextAsUser(user: UserHandle, flags: Int) = if (user == UserHandle.ALL) mockContext else this + override fun getSystemService(name: String) = + if (name == Context.TELEPHONY_SERVICE) telephonyManager + else super.getSystemService(name) } - private inner class WrappedNotificationUpdater(c: Context) : TetheringNotificationUpdater(c) { + private inner class WrappedNotificationUpdater(c: Context, looper: Looper) + : TetheringNotificationUpdater(c, looper) { override fun getResourcesForSubId(context: Context, subId: Int) = - if (subId == TEST_SUBID) testResources else defaultResources + when (subId) { + TEST_SUBID -> testResources + INVALID_SUBSCRIPTION_ID -> defaultResources + else -> super.getResourcesForSubId(context, subId) + } } private fun setupResources() { @@ -94,12 +120,20 @@ class TetheringNotificationUpdaterTest { .getStringArray(R.array.tethering_notification_icons) doReturn(arrayOf("WIFI;android.test:drawable/wifi")).`when`(testResources) .getStringArray(R.array.tethering_notification_icons) - doReturn(TITTLE).`when`(defaultResources).getString(R.string.tethering_notification_title) + doReturn(5).`when`(testResources) + .getInteger(R.integer.delay_to_show_no_upstream_after_no_backhaul) + doReturn(TITLE).`when`(defaultResources).getString(R.string.tethering_notification_title) doReturn(MESSAGE).`when`(defaultResources) .getString(R.string.tethering_notification_message) - doReturn(TEST_TITTLE).`when`(testResources).getString(R.string.tethering_notification_title) + doReturn(TEST_TITLE).`when`(testResources).getString(R.string.tethering_notification_title) doReturn(TEST_MESSAGE).`when`(testResources) .getString(R.string.tethering_notification_message) + doReturn(TEST_NO_UPSTREAM_TITLE).`when`(testResources) + .getString(R.string.no_upstream_notification_title) + doReturn(TEST_NO_UPSTREAM_MESSAGE).`when`(testResources) + .getString(R.string.no_upstream_notification_message) + doReturn(TEST_NO_UPSTREAM_BUTTON).`when`(testResources) + .getString(R.string.no_upstream_notification_disable_button) doReturn(USB_ICON_ID).`when`(defaultResources) .getIdentifier(eq("android.test:drawable/usb"), any(), any()) doReturn(BT_ICON_ID).`when`(defaultResources) @@ -113,35 +147,61 @@ class TetheringNotificationUpdaterTest { @Before fun setUp() { MockitoAnnotations.initMocks(this) - val context = TestContext(InstrumentationRegistry.getInstrumentation().context) + context = TestContext(InstrumentationRegistry.getInstrumentation().context) doReturn(notificationManager).`when`(mockContext) .getSystemService(Context.NOTIFICATION_SERVICE) - notificationUpdater = WrappedNotificationUpdater(context) + fakeTetheringThread = HandlerThread(this::class.simpleName) + fakeTetheringThread.start() + notificationUpdater = WrappedNotificationUpdater(context, fakeTetheringThread.looper) setupResources() } + @After + fun tearDown() { + fakeTetheringThread.quitSafely() + } + private fun Notification.title() = this.extras.getString(Notification.EXTRA_TITLE) private fun Notification.text() = this.extras.getString(Notification.EXTRA_TEXT) - private fun verifyNotification(iconId: Int = 0, title: String = "", text: String = "") { - verify(notificationManager, never()).cancel(any(), anyInt()) + private fun verifyNotification(iconId: Int, title: String, text: String, id: Int) { + verify(notificationManager, never()).cancel(any(), eq(id)) val notificationCaptor = ArgumentCaptor.forClass(Notification::class.java) verify(notificationManager, times(1)) - .notify(any(), anyInt(), notificationCaptor.capture()) + .notify(any(), eq(id), notificationCaptor.capture()) val notification = notificationCaptor.getValue() assertEquals(iconId, notification.smallIcon.resId) assertEquals(title, notification.title()) assertEquals(text, notification.text()) + } + private fun verifyNotificationCancelled(id: Int) = + verify(notificationManager, times(1)).cancel(any(), eq(id)) + + private val tetheringActiveNotifications = + listOf(NO_UPSTREAM_NOTIFICATION_ID, ENABLE_NOTIFICATION_ID) + + private fun verifyCancelAllTetheringActiveNotifications() { + tetheringActiveNotifications.forEach { + verifyNotificationCancelled(it) + } reset(notificationManager) } - private fun verifyNoNotification() { - verify(notificationManager, times(1)).cancel(any(), anyInt()) - verify(notificationManager, never()).notify(any(), anyInt(), any()) - + private fun verifyOnlyTetheringActiveNotification( + notifyId: Int, + iconId: Int, + title: String, + text: String + ) { + tetheringActiveNotifications.forEach { + when (it) { + notifyId -> verifyNotification(iconId, title, text, notifyId) + else -> verifyNotificationCancelled(it) + } + } reset(notificationManager) } @@ -149,7 +209,7 @@ class TetheringNotificationUpdaterTest { fun testNotificationWithDownstreamChanged() { // Wifi downstream. No notification. notificationUpdater.onDownstreamChanged(WIFI_MASK) - verifyNoNotification() + verifyCancelAllTetheringActiveNotifications() // Same downstream changed. Nothing happened. notificationUpdater.onDownstreamChanged(WIFI_MASK) @@ -157,22 +217,23 @@ class TetheringNotificationUpdaterTest { // Wifi and usb downstreams. Show enable notification notificationUpdater.onDownstreamChanged(WIFI_MASK or USB_MASK) - verifyNotification(GENERAL_ICON_ID, TITTLE, MESSAGE) + verifyOnlyTetheringActiveNotification( + ENABLE_NOTIFICATION_ID, GENERAL_ICON_ID, TITLE, MESSAGE) // Usb downstream. Still show enable notification. notificationUpdater.onDownstreamChanged(USB_MASK) - verifyNotification(USB_ICON_ID, TITTLE, MESSAGE) + verifyOnlyTetheringActiveNotification(ENABLE_NOTIFICATION_ID, USB_ICON_ID, TITLE, MESSAGE) // No downstream. No notification. notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE) - verifyNoNotification() + verifyCancelAllTetheringActiveNotifications() } @Test fun testNotificationWithActiveDataSubscriptionIdChanged() { // Usb downstream. Showed enable notification with default resource. notificationUpdater.onDownstreamChanged(USB_MASK) - verifyNotification(USB_ICON_ID, TITTLE, MESSAGE) + verifyOnlyTetheringActiveNotification(ENABLE_NOTIFICATION_ID, USB_ICON_ID, TITLE, MESSAGE) // Same subId changed. Nothing happened. notificationUpdater.onActiveDataSubscriptionIdChanged(INVALID_SUBSCRIPTION_ID) @@ -180,15 +241,16 @@ class TetheringNotificationUpdaterTest { // Set test sub id. Clear notification with test resource. notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID) - verifyNoNotification() + verifyCancelAllTetheringActiveNotifications() // Wifi downstream. Show enable notification with test resource. notificationUpdater.onDownstreamChanged(WIFI_MASK) - verifyNotification(WIFI_ICON_ID, TEST_TITTLE, TEST_MESSAGE) + verifyOnlyTetheringActiveNotification( + ENABLE_NOTIFICATION_ID, WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE) // No downstream. No notification. notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE) - verifyNoNotification() + verifyCancelAllTetheringActiveNotifications() } private fun assertIconNumbers(number: Int, configs: Array) { @@ -227,10 +289,8 @@ class TetheringNotificationUpdaterTest { @Test fun testSetupRestrictedNotification() { - val title = InstrumentationRegistry.getInstrumentation().context.resources - .getString(R.string.disable_tether_notification_title) - val message = InstrumentationRegistry.getInstrumentation().context.resources - .getString(R.string.disable_tether_notification_message) + val title = context.resources.getString(R.string.disable_tether_notification_title) + val message = context.resources.getString(R.string.disable_tether_notification_message) val disallowTitle = "Tether function is disallowed" val disallowMessage = "Please contact your admin" doReturn(title).`when`(defaultResources) @@ -244,18 +304,127 @@ class TetheringNotificationUpdaterTest { // User restrictions on. Show restricted notification. notificationUpdater.notifyTetheringDisabledByRestriction() - verifyNotification(R.drawable.stat_sys_tether_general, title, message) + verifyNotification(R.drawable.stat_sys_tether_general, title, message, + RESTRICTED_NOTIFICATION_ID) + reset(notificationManager) // User restrictions off. Clear notification. notificationUpdater.tetheringRestrictionLifted() - verifyNoNotification() + verifyNotificationCancelled(RESTRICTED_NOTIFICATION_ID) + reset(notificationManager) // Set test sub id. No notification. notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID) - verifyNoNotification() + verifyCancelAllTetheringActiveNotifications() // User restrictions on again. Show restricted notification with test resource. notificationUpdater.notifyTetheringDisabledByRestriction() - verifyNotification(R.drawable.stat_sys_tether_general, disallowTitle, disallowMessage) + verifyNotification(R.drawable.stat_sys_tether_general, disallowTitle, disallowMessage, + RESTRICTED_NOTIFICATION_ID) + reset(notificationManager) + } + + val MAX_BACKOFF_MS = 200L + /** + * Waits for all messages, including delayed ones, to be processed. + * + * This will wait until the handler has no more messages to be processed including + * delayed ones, or the timeout has expired. It uses an exponential backoff strategy + * to wait longer and longer to consume less CPU, with the max granularity being + * MAX_BACKOFF_MS. + * + * @return true if all messages have been processed including delayed ones, false if timeout + * + * TODO: Move this method to com.android.testutils.HandlerUtils.kt. + */ + private fun Handler.waitForDelayedMessage(what: Int?, timeoutMs: Long) { + fun hasMatchingMessages() = + if (what == null) hasMessagesOrCallbacks() else hasMessages(what) + val expiry = System.currentTimeMillis() + timeoutMs + var delay = 5L + while (System.currentTimeMillis() < expiry && hasMatchingMessages()) { + // None of Handler, Looper, Message and MessageQueue expose any way to retrieve + // the time when the next (let alone the last) message will be processed, so + // short of examining the internals with reflection sleep() is the only solution. + Thread.sleep(delay) + delay = (delay * 2) + .coerceAtMost(expiry - System.currentTimeMillis()) + .coerceAtMost(MAX_BACKOFF_MS) + } + + val timeout = expiry - System.currentTimeMillis() + if (timeout <= 0) fail("Delayed message did not process yet after ${timeoutMs}ms") + waitForIdle(timeout) + } + + @Test + fun testNotificationWithUpstreamNetworkChanged() { + // Set test sub id. No notification. + notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID) + verifyCancelAllTetheringActiveNotifications() + + // Wifi downstream. Show enable notification with test resource. + notificationUpdater.onDownstreamChanged(WIFI_MASK) + verifyOnlyTetheringActiveNotification( + ENABLE_NOTIFICATION_ID, WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE) + + // There is no upstream. Show no upstream notification. + notificationUpdater.onUpstreamNetworkChanged(null) + notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, 500L) + verifyNotification(R.drawable.stat_sys_tether_general, TEST_NO_UPSTREAM_TITLE, + TEST_NO_UPSTREAM_MESSAGE, NO_UPSTREAM_NOTIFICATION_ID) + reset(notificationManager) + + // Same upstream network changed. Nothing happened. + notificationUpdater.onUpstreamNetworkChanged(null) + verifyZeroInteractions(notificationManager) + + // Upstream come back. Clear no upstream notification. + notificationUpdater.onUpstreamNetworkChanged(Network(1000)) + verifyNotificationCancelled(NO_UPSTREAM_NOTIFICATION_ID) + reset(notificationManager) + + // No upstream again. Show no upstream notification. + notificationUpdater.onUpstreamNetworkChanged(null) + notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, 500L) + verifyNotification(R.drawable.stat_sys_tether_general, TEST_NO_UPSTREAM_TITLE, + TEST_NO_UPSTREAM_MESSAGE, NO_UPSTREAM_NOTIFICATION_ID) + reset(notificationManager) + + // No downstream. No notification. + notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE) + verifyCancelAllTetheringActiveNotifications() + + // Set R.integer.delay_to_show_no_upstream_after_no_backhaul to 0 and have wifi downstream + // again. Show enable notification only. + doReturn(-1).`when`(testResources) + .getInteger(R.integer.delay_to_show_no_upstream_after_no_backhaul) + notificationUpdater.onDownstreamChanged(WIFI_MASK) + notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, 500L) + verifyOnlyTetheringActiveNotification( + ENABLE_NOTIFICATION_ID, WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE) + } + + @Test + fun testGetResourcesForSubId() { + doReturn(telephonyManager).`when`(telephonyManager).createForSubscriptionId(anyInt()) + doReturn(1234).`when`(telephonyManager).getSimCarrierId() + doReturn("000000").`when`(telephonyManager).getSimOperator() + + val subId = -2 // Use invalid subId to avoid getting resource from cache or real subId. + val config = context.resources.configuration + var res = notificationUpdater.getResourcesForSubId(context, subId) + assertEquals(config.mcc, res.configuration.mcc) + assertEquals(config.mnc, res.configuration.mnc) + + doReturn(1839).`when`(telephonyManager).getSimCarrierId() + res = notificationUpdater.getResourcesForSubId(context, subId) + assertEquals(config.mcc, res.configuration.mcc) + assertEquals(config.mnc, res.configuration.mnc) + + doReturn("20404").`when`(telephonyManager).getSimOperator() + res = notificationUpdater.getResourcesForSubId(context, subId) + assertEquals(311, res.configuration.mcc) + assertEquals(480, res.configuration.mnc) } } 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 d4be3a26d9..feb99e6b24 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java @@ -383,7 +383,7 @@ public class TetheringTest { } @Override - public TetheringNotificationUpdater getNotificationUpdater(Context ctx) { + public TetheringNotificationUpdater getNotificationUpdater(Context ctx, Looper looper) { return mNotificationUpdater; } } @@ -1691,6 +1691,18 @@ public class TetheringTest { assertEquals(clientAddrParceled, params.clientAddr); } + @Test + public void testUpstreamNetworkChanged() { + final Tethering.TetherMasterSM stateMachine = (Tethering.TetherMasterSM) + mTetheringDependencies.mUpstreamNetworkMonitorMasterSM; + final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState(); + when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())).thenReturn(upstreamState); + stateMachine.chooseUpstreamType(true); + + verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(eq(upstreamState.network)); + verify(mNotificationUpdater, times(1)).onUpstreamNetworkChanged(eq(upstreamState.network)); + } + // TODO: Test that a request for hotspot mode doesn't interfere with an // already operating tethering mode interface. } From 30c0da71b0f49a842d11483856d9950aed844c7e Mon Sep 17 00:00:00 2001 From: Mark Chien Date: Thu, 16 Apr 2020 02:47:22 +0000 Subject: [PATCH 102/188] Remove sendMessage inside EntitlementManager EntitlementManager and its callers(Tethering and UpstreamNetworkMonitor) run in the same threads. Bug: 141256482 Test: atest TetheringTests Merged-In: I0a376d28b123eaab2e8d00a98a4719ce983d3bb2 Change-Id: I0a376d28b123eaab2e8d00a98a4719ce983d3bb2 --- .../tethering/EntitlementManager.java | 92 +++---------------- .../networkstack/tethering/Tethering.java | 6 +- 2 files changed, 15 insertions(+), 83 deletions(-) diff --git a/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java b/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java index 4c7b2d49ee..049a9f68bb 100644 --- a/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java +++ b/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java @@ -38,8 +38,6 @@ import android.content.IntentFilter; import android.net.util.SharedLog; import android.os.Bundle; import android.os.Handler; -import android.os.Looper; -import android.os.Message; import android.os.Parcel; import android.os.PersistableBundle; import android.os.ResultReceiver; @@ -75,11 +73,6 @@ public class EntitlementManager { private final ComponentName mSilentProvisioningService; private static final int MS_PER_HOUR = 60 * 60 * 1000; - private static final int EVENT_START_PROVISIONING = 0; - private static final int EVENT_STOP_PROVISIONING = 1; - private static final int EVENT_UPSTREAM_CHANGED = 2; - private static final int EVENT_MAYBE_RUN_PROVISIONING = 3; - private static final int EVENT_GET_ENTITLEMENT_VALUE = 4; // The ArraySet contains enabled downstream types, ex: // {@link TetheringManager.TETHERING_WIFI} @@ -90,7 +83,7 @@ public class EntitlementManager { private final int mPermissionChangeMessageCode; private final SharedLog mLog; private final SparseIntArray mEntitlementCacheValue; - private final EntitlementHandler mHandler; + private final Handler mHandler; private final StateMachine mTetherMasterSM; // Key: TetheringManager.TETHERING_*(downstream). // Value: TetheringManager.TETHER_ERROR_{NO_ERROR or PROVISION_FAILED}(provisioning result). @@ -112,10 +105,7 @@ public class EntitlementManager { mEntitlementCacheValue = new SparseIntArray(); mTetherMasterSM = tetherMasterSM; mPermissionChangeMessageCode = permissionChangeMessageCode; - final Handler masterHandler = tetherMasterSM.getHandler(); - // Create entitlement's own handler which is associated with TetherMaster thread - // let all entitlement processes run in the same thread. - mHandler = new EntitlementHandler(masterHandler.getLooper()); + mHandler = tetherMasterSM.getHandler(); mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_PROVISIONING_ALARM), null, mHandler); mSilentProvisioningService = ComponentName.unflattenFromString( @@ -172,14 +162,9 @@ public class EntitlementManager { * provisioning app UI if there is one. */ public void startProvisioningIfNeeded(int downstreamType, boolean showProvisioningUi) { - mHandler.sendMessage(mHandler.obtainMessage(EVENT_START_PROVISIONING, - downstreamType, encodeBool(showProvisioningUi))); - } + if (!isValidDownstreamType(downstreamType)) return; - private void handleStartProvisioningIfNeeded(int type, boolean showProvisioningUi) { - if (!isValidDownstreamType(type)) return; - - if (!mCurrentTethers.contains(type)) mCurrentTethers.add(type); + if (!mCurrentTethers.contains(downstreamType)) mCurrentTethers.add(downstreamType); final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); if (isTetherProvisioningRequired(config)) { @@ -192,9 +177,9 @@ public class EntitlementManager { // till upstream change to cellular. if (mUsingCellularAsUpstream) { if (showProvisioningUi) { - runUiTetherProvisioning(type, config.activeDataSubId); + runUiTetherProvisioning(downstreamType, config.activeDataSubId); } else { - runSilentTetherProvisioning(type, config.activeDataSubId); + runSilentTetherProvisioning(downstreamType, config.activeDataSubId); } mNeedReRunProvisioningUi = false; } else { @@ -211,10 +196,6 @@ public class EntitlementManager { * @param type tethering type from TetheringManager.TETHERING_{@code *} */ public void stopProvisioningIfNeeded(int type) { - mHandler.sendMessage(mHandler.obtainMessage(EVENT_STOP_PROVISIONING, type, 0)); - } - - private void handleStopProvisioningIfNeeded(int type) { if (!isValidDownstreamType(type)) return; mCurrentTethers.remove(type); @@ -230,11 +211,6 @@ public class EntitlementManager { * @param isCellular whether tethering upstream is cellular. */ public void notifyUpstream(boolean isCellular) { - mHandler.sendMessage(mHandler.obtainMessage( - EVENT_UPSTREAM_CHANGED, encodeBool(isCellular), 0)); - } - - private void handleNotifyUpstream(boolean isCellular) { if (DBG) { mLog.i("notifyUpstream: " + isCellular + ", mCellularUpstreamPermitted: " + mCellularUpstreamPermitted @@ -244,16 +220,17 @@ public class EntitlementManager { if (mUsingCellularAsUpstream) { final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); - handleMaybeRunProvisioning(config); + maybeRunProvisioning(config); } } /** Run provisioning if needed */ public void maybeRunProvisioning() { - mHandler.sendMessage(mHandler.obtainMessage(EVENT_MAYBE_RUN_PROVISIONING)); + final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); + maybeRunProvisioning(config); } - private void handleMaybeRunProvisioning(final TetheringConfiguration config) { + private void maybeRunProvisioning(final TetheringConfiguration config) { if (mCurrentTethers.size() == 0 || !isTetherProvisioningRequired(config)) { return; } @@ -319,7 +296,7 @@ public class EntitlementManager { } if (mUsingCellularAsUpstream) { - handleMaybeRunProvisioning(config); + maybeRunProvisioning(config); } } @@ -494,46 +471,6 @@ public class EntitlementManager { } }; - private class EntitlementHandler extends Handler { - EntitlementHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case EVENT_START_PROVISIONING: - handleStartProvisioningIfNeeded(msg.arg1, toBool(msg.arg2)); - break; - case EVENT_STOP_PROVISIONING: - handleStopProvisioningIfNeeded(msg.arg1); - break; - case EVENT_UPSTREAM_CHANGED: - handleNotifyUpstream(toBool(msg.arg1)); - break; - case EVENT_MAYBE_RUN_PROVISIONING: - final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); - handleMaybeRunProvisioning(config); - break; - case EVENT_GET_ENTITLEMENT_VALUE: - handleRequestLatestTetheringEntitlementValue(msg.arg1, - (ResultReceiver) msg.obj, toBool(msg.arg2)); - break; - default: - mLog.log("Unknown event: " + msg.what); - break; - } - } - } - - private static boolean toBool(int encodedBoolean) { - return encodedBoolean != 0; - } - - private static int encodeBool(boolean b) { - return b ? 1 : 0; - } - private static boolean isValidDownstreamType(int type) { switch (type) { case TETHERING_BLUETOOTH: @@ -644,13 +581,6 @@ public class EntitlementManager { /** Get the last value of the tethering entitlement check. */ public void requestLatestTetheringEntitlementResult(int downstream, ResultReceiver receiver, boolean showEntitlementUi) { - mHandler.sendMessage(mHandler.obtainMessage(EVENT_GET_ENTITLEMENT_VALUE, - downstream, encodeBool(showEntitlementUi), receiver)); - - } - - private void handleRequestLatestTetheringEntitlementValue(int downstream, - ResultReceiver receiver, boolean showEntitlementUi) { if (!isValidDownstreamType(downstream)) { receiver.send(TETHER_ERROR_ENTITLEMENT_UNKNOWN, null); return; diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java index bae54a5c76..f8453edbea 100644 --- a/Tethering/src/com/android/networkstack/tethering/Tethering.java +++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java @@ -1946,10 +1946,12 @@ public class Tethering { /** Get the latest value of the tethering entitlement check. */ void requestLatestTetheringEntitlementResult(int type, ResultReceiver receiver, boolean showEntitlementUi) { - if (receiver != null) { + if (receiver == null) return; + + mHandler.post(() -> { mEntitlementMgr.requestLatestTetheringEntitlementResult(type, receiver, showEntitlementUi); - } + }); } /** Register tethering event callback */ From 5e9c40c01276252f00c4c8f363631c22bbeb2ece Mon Sep 17 00:00:00 2001 From: Paul Hu Date: Thu, 16 Apr 2020 09:39:28 +0000 Subject: [PATCH 103/188] [TNU05.1] Address aosp/1237036 leftover comments Bug: 147818698 Test: atest TetheringTests Change-Id: Ife738339aeae00d2063fea6918b50204daef24fc Merged-In: Ife738339aeae00d2063fea6918b50204daef24fc (cherry picked from commit c638ae6436509df092ab8814bb5b64591878229d, aosp/1287133) --- .../TetheringNotificationUpdater.java | 18 ++++++++---------- .../TetheringNotificationUpdaterTest.kt | 3 ++- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java b/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java index ff83fd1e4f..de2f90e50e 100644 --- a/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java @@ -50,9 +50,6 @@ import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; -import java.util.Arrays; -import java.util.List; - /** * A class to display tethering-related notifications. * @@ -89,6 +86,9 @@ public class TetheringNotificationUpdater { static final int NO_ICON_ID = 0; @VisibleForTesting static final int DOWNSTREAM_NONE = 0; + // Refer to TelephonyManager#getSimCarrierId for more details about carrier id. + @VisibleForTesting + static final int VERIZON_CARRIER_ID = 1839; private final Context mContext; private final NotificationManager mNotificationManager; private final NotificationChannel mChannel; @@ -114,11 +114,11 @@ public class TetheringNotificationUpdater { @interface NotificationId {} private static final class MccMncOverrideInfo { - public final List visitedMccMncs; + public final String visitedMccMnc; public final int homeMcc; public final int homeMnc; - MccMncOverrideInfo(List visitedMccMncs, int mcc, int mnc) { - this.visitedMccMncs = visitedMccMncs; + MccMncOverrideInfo(String visitedMccMnc, int mcc, int mnc) { + this.visitedMccMnc = visitedMccMnc; this.homeMcc = mcc; this.homeMnc = mnc; } @@ -127,9 +127,7 @@ public class TetheringNotificationUpdater { private static final SparseArray sCarrierIdToMccMnc = new SparseArray<>(); static { - // VZW - sCarrierIdToMccMnc.put( - 1839, new MccMncOverrideInfo(Arrays.asList(new String[] {"20404"}), 311, 480)); + sCarrierIdToMccMnc.put(VERIZON_CARRIER_ID, new MccMncOverrideInfo("20404", 311, 480)); } public TetheringNotificationUpdater(@NonNull final Context context, @@ -200,7 +198,7 @@ public class TetheringNotificationUpdater { final int carrierId = tm.getSimCarrierId(); final String mccmnc = tm.getSimOperator(); final MccMncOverrideInfo overrideInfo = sCarrierIdToMccMnc.get(carrierId); - if (overrideInfo != null && overrideInfo.visitedMccMncs.contains(mccmnc)) { + if (overrideInfo != null && overrideInfo.visitedMccMnc.equals(mccmnc)) { // Re-configure MCC/MNC value to specific carrier to get right resources. final Configuration config = res.getConfiguration(); config.mcc = overrideInfo.homeMcc; diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt index 5f8858857c..294bf1b7e1 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt @@ -39,6 +39,7 @@ import com.android.networkstack.tethering.TetheringNotificationUpdater.ENABLE_NO import com.android.networkstack.tethering.TetheringNotificationUpdater.EVENT_SHOW_NO_UPSTREAM import com.android.networkstack.tethering.TetheringNotificationUpdater.NO_UPSTREAM_NOTIFICATION_ID import com.android.networkstack.tethering.TetheringNotificationUpdater.RESTRICTED_NOTIFICATION_ID +import com.android.networkstack.tethering.TetheringNotificationUpdater.VERIZON_CARRIER_ID import com.android.testutils.waitForIdle import org.junit.After import org.junit.Assert.assertEquals @@ -417,7 +418,7 @@ class TetheringNotificationUpdaterTest { assertEquals(config.mcc, res.configuration.mcc) assertEquals(config.mnc, res.configuration.mnc) - doReturn(1839).`when`(telephonyManager).getSimCarrierId() + doReturn(VERIZON_CARRIER_ID).`when`(telephonyManager).getSimCarrierId() res = notificationUpdater.getResourcesForSubId(context, subId) assertEquals(config.mcc, res.configuration.mcc) assertEquals(config.mnc, res.configuration.mnc) From 4580c83253b9075c763d56f9b4bea5349b3eeef7 Mon Sep 17 00:00:00 2001 From: Chalard Jean Date: Fri, 17 Apr 2020 17:12:44 +0000 Subject: [PATCH 104/188] [TNU06] Add roaming notification Warn user of potential data charges if the backhaul is cellular and user is roaming. Bug: 145629001 Test: atest TetheringTests Change-Id: I74b4f87c2f6aad09e05d3f2a779f880396885953 Merged-In: I74b4f87c2f6aad09e05d3f2a779f880396885953 (cherry picked from commit 1af69e5b8f339bde5b70886d80960ce22c847245, aosp/1237026) --- Tethering/res/values-mcc310-mnc004/config.xml | 3 + Tethering/res/values-mcc311-mnc480/config.xml | 3 + Tethering/res/values/config.xml | 5 + .../networkstack/tethering/Tethering.java | 17 +- .../TetheringNotificationUpdater.java | 102 +++++++--- .../TetheringNotificationUpdaterTest.kt | 186 +++++++++++------- .../networkstack/tethering/TetheringTest.java | 25 ++- 7 files changed, 247 insertions(+), 94 deletions(-) diff --git a/Tethering/res/values-mcc310-mnc004/config.xml b/Tethering/res/values-mcc310-mnc004/config.xml index 8c627d5df0..5c5be0466a 100644 --- a/Tethering/res/values-mcc310-mnc004/config.xml +++ b/Tethering/res/values-mcc310-mnc004/config.xml @@ -17,4 +17,7 @@ 5000 + + + true \ No newline at end of file diff --git a/Tethering/res/values-mcc311-mnc480/config.xml b/Tethering/res/values-mcc311-mnc480/config.xml index 8c627d5df0..5c5be0466a 100644 --- a/Tethering/res/values-mcc311-mnc480/config.xml +++ b/Tethering/res/values-mcc311-mnc480/config.xml @@ -17,4 +17,7 @@ 5000 + + + true \ No newline at end of file diff --git a/Tethering/res/values/config.xml b/Tethering/res/values/config.xml index 52aa5bbaff..66fbefca94 100644 --- a/Tethering/res/values/config.xml +++ b/Tethering/res/values/config.xml @@ -208,4 +208,9 @@ -1 + + + + false diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java index da8bf54718..05cf68efd7 100644 --- a/Tethering/src/com/android/networkstack/tethering/Tethering.java +++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java @@ -81,6 +81,7 @@ import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.Network; +import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.TetherStatesParcel; import android.net.TetheredClient; @@ -1476,7 +1477,7 @@ public class Tethering { if (mTetherUpstream != newUpstream) { mTetherUpstream = newUpstream; mUpstreamNetworkMonitor.setCurrentUpstream(mTetherUpstream); - reportUpstreamChanged(mTetherUpstream); + reportUpstreamChanged(ns); } } @@ -1598,7 +1599,8 @@ public class Tethering { } } - private void handleUpstreamNetworkMonitorCallback(int arg1, Object o) { + @VisibleForTesting + void handleUpstreamNetworkMonitorCallback(int arg1, Object o) { if (arg1 == UpstreamNetworkMonitor.NOTIFY_LOCAL_PREFIXES) { mOffload.sendOffloadExemptPrefixes((Set) o); return; @@ -1624,6 +1626,9 @@ public class Tethering { switch (arg1) { case UpstreamNetworkMonitor.EVENT_ON_CAPABILITIES: + if (ns.network.equals(mTetherUpstream)) { + mNotificationUpdater.onUpstreamCapabilitiesChanged(ns.networkCapabilities); + } handleNewUpstreamNetworkState(ns); break; case UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES: @@ -2009,8 +2014,10 @@ public class Tethering { }); } - private void reportUpstreamChanged(Network network) { + private void reportUpstreamChanged(UpstreamNetworkState ns) { final int length = mTetheringEventCallbacks.beginBroadcast(); + final Network network = (ns != null) ? ns.network : null; + final NetworkCapabilities capabilities = (ns != null) ? ns.networkCapabilities : null; try { for (int i = 0; i < length; i++) { try { @@ -2022,7 +2029,9 @@ public class Tethering { } finally { mTetheringEventCallbacks.finishBroadcast(); } - mNotificationUpdater.onUpstreamNetworkChanged(network); + // Need to notify capabilities change after upstream network changed because new network's + // capabilities should be checked every time. + mNotificationUpdater.onUpstreamCapabilitiesChanged(capabilities); } private void reportConfigurationChanged(TetheringConfigurationParcel config) { diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java b/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java index de2f90e50e..f490cc4719 100644 --- a/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java @@ -16,6 +16,7 @@ package com.android.networkstack.tethering; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; import static android.net.TetheringManager.TETHERING_BLUETOOTH; import static android.net.TetheringManager.TETHERING_USB; import static android.net.TetheringManager.TETHERING_WIFI; @@ -30,7 +31,7 @@ import android.content.Context; import android.content.Intent; import android.content.res.Configuration; import android.content.res.Resources; -import android.net.Network; +import android.net.NetworkCapabilities; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -50,6 +51,9 @@ import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * A class to display tethering-related notifications. * @@ -82,6 +86,9 @@ public class TetheringNotificationUpdater { // Id to update and cancel no upstream notification. Must be unique within the tethering app. @VisibleForTesting static final int NO_UPSTREAM_NOTIFICATION_ID = 1002; + // Id to update and cancel roaming notification. Must be unique within the tethering app. + @VisibleForTesting + static final int ROAMING_NOTIFICATION_ID = 1003; @VisibleForTesting static final int NO_ICON_ID = 0; @VisibleForTesting @@ -95,13 +102,14 @@ public class TetheringNotificationUpdater { private final Handler mHandler; // WARNING : the constructor is called on a different thread. Thread safety therefore - // relies on these values being initialized to 0 or false, and not any other value. If you need - // to change this, you will need to change the thread where the constructor is invoked, - // or to introduce synchronization. + // relies on these values being initialized to 0, false or null, and not any other value. If you + // need to change this, you will need to change the thread where the constructor is invoked, or + // to introduce synchronization. // Downstream type is one of ConnectivityManager.TETHERING_* constants, 0 1 or 2. // This value has to be made 1 2 and 4, and OR'd with the others. private int mDownstreamTypesMask = DOWNSTREAM_NONE; private boolean mNoUpstream = false; + private boolean mRoaming = false; // WARNING : this value is not able to being initialized to 0 and must have volatile because // telephony service is not guaranteed that is up before tethering service starts. If telephony @@ -110,7 +118,13 @@ public class TetheringNotificationUpdater { // INVALID_SUBSCRIPTION_ID. private volatile int mActiveDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; - @IntDef({ENABLE_NOTIFICATION_ID, RESTRICTED_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID}) + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = { + ENABLE_NOTIFICATION_ID, + RESTRICTED_NOTIFICATION_ID, + NO_UPSTREAM_NOTIFICATION_ID, + ROAMING_NOTIFICATION_ID + }) @interface NotificationId {} private static final class MccMncOverrideInfo { @@ -160,26 +174,22 @@ public class TetheringNotificationUpdater { /** Called when downstream has changed */ public void onDownstreamChanged(@IntRange(from = 0, to = 7) final int downstreamTypesMask) { - if (mDownstreamTypesMask == downstreamTypesMask) return; - mDownstreamTypesMask = downstreamTypesMask; - updateEnableNotification(); - updateNoUpstreamNotification(); + updateActiveNotifications( + mActiveDataSubId, downstreamTypesMask, mNoUpstream, mRoaming); } /** Called when active data subscription id changed */ public void onActiveDataSubscriptionIdChanged(final int subId) { - if (mActiveDataSubId == subId) return; - mActiveDataSubId = subId; - updateEnableNotification(); - updateNoUpstreamNotification(); + updateActiveNotifications(subId, mDownstreamTypesMask, mNoUpstream, mRoaming); } - /** Called when upstream network changed */ - public void onUpstreamNetworkChanged(@Nullable final Network network) { - final boolean isNoUpstream = (network == null); - if (mNoUpstream == isNoUpstream) return; - mNoUpstream = isNoUpstream; - updateNoUpstreamNotification(); + /** Called when upstream network capabilities changed */ + public void onUpstreamCapabilitiesChanged(@Nullable final NetworkCapabilities capabilities) { + final boolean isNoUpstream = (capabilities == null); + final boolean isRoaming = capabilities != null + && !capabilities.hasCapability(NET_CAPABILITY_NOT_ROAMING); + updateActiveNotifications( + mActiveDataSubId, mDownstreamTypesMask, isNoUpstream, isRoaming); } @NonNull @@ -208,6 +218,25 @@ public class TetheringNotificationUpdater { return res; } + private void updateActiveNotifications(final int subId, final int downstreamTypes, + final boolean noUpstream, final boolean isRoaming) { + final boolean tetheringActiveChanged = + (downstreamTypes == DOWNSTREAM_NONE) != (mDownstreamTypesMask == DOWNSTREAM_NONE); + final boolean subIdChanged = subId != mActiveDataSubId; + final boolean downstreamChanged = downstreamTypes != mDownstreamTypesMask; + final boolean upstreamChanged = noUpstream != mNoUpstream; + final boolean roamingChanged = isRoaming != mRoaming; + final boolean updateAll = tetheringActiveChanged || subIdChanged; + mActiveDataSubId = subId; + mDownstreamTypesMask = downstreamTypes; + mNoUpstream = noUpstream; + mRoaming = isRoaming; + + if (updateAll || downstreamChanged) updateEnableNotification(); + if (updateAll || upstreamChanged) updateNoUpstreamNotification(); + if (updateAll || roamingChanged) updateRoamingNotification(); + } + private void updateEnableNotification() { final boolean tetheringInactive = mDownstreamTypesMask == DOWNSTREAM_NONE; @@ -219,14 +248,20 @@ public class TetheringNotificationUpdater { private void updateNoUpstreamNotification() { final boolean tetheringInactive = mDownstreamTypesMask == DOWNSTREAM_NONE; - if (tetheringInactive - || !mNoUpstream - || setupNoUpstreamNotification() == NO_NOTIFY) { + if (tetheringInactive || !mNoUpstream || setupNoUpstreamNotification() == NO_NOTIFY) { clearNotification(NO_UPSTREAM_NOTIFICATION_ID); mHandler.removeMessages(EVENT_SHOW_NO_UPSTREAM); } } + private void updateRoamingNotification() { + final boolean tetheringInactive = mDownstreamTypesMask == DOWNSTREAM_NONE; + + if (tetheringInactive || !mRoaming || setupRoamingNotification() == NO_NOTIFY) { + clearNotification(ROAMING_NOTIFICATION_ID); + } + } + @VisibleForTesting void tetheringRestrictionLifted() { clearNotification(RESTRICTED_NOTIFICATION_ID); @@ -333,6 +368,29 @@ public class TetheringNotificationUpdater { return icons; } + private boolean setupRoamingNotification() { + final Resources res = getResourcesForSubId(mContext, mActiveDataSubId); + final boolean upstreamRoamingNotification = + res.getBoolean(R.bool.config_upstream_roaming_notification); + + if (!upstreamRoamingNotification) return NO_NOTIFY; + + final String title = res.getString(R.string.upstream_roaming_notification_title); + final String message = res.getString(R.string.upstream_roaming_notification_message); + if (isEmpty(title) || isEmpty(message)) return NO_NOTIFY; + + final PendingIntent pi = PendingIntent.getActivity( + mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */), + 0 /* requestCode */, + new Intent(Settings.ACTION_TETHER_SETTINGS), + Intent.FLAG_ACTIVITY_NEW_TASK, + null /* options */); + + showNotification(R.drawable.stat_sys_tether_general, title, message, + ROAMING_NOTIFICATION_ID, pi, new Action[0]); + return NOTIFY_DONE; + } + private boolean setupNoUpstreamNotification() { final Resources res = getResourcesForSubId(mContext, mActiveDataSubId); final int delayToShowUpstreamNotification = diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt index 294bf1b7e1..04f31a7a28 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt @@ -23,10 +23,11 @@ import android.content.res.Resources import android.net.ConnectivityManager.TETHERING_BLUETOOTH import android.net.ConnectivityManager.TETHERING_USB import android.net.ConnectivityManager.TETHERING_WIFI -import android.net.Network import android.os.Handler import android.os.HandlerThread import android.os.Looper +import android.net.NetworkCapabilities +import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING import android.os.UserHandle import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID import android.telephony.TelephonyManager @@ -39,6 +40,7 @@ import com.android.networkstack.tethering.TetheringNotificationUpdater.ENABLE_NO import com.android.networkstack.tethering.TetheringNotificationUpdater.EVENT_SHOW_NO_UPSTREAM import com.android.networkstack.tethering.TetheringNotificationUpdater.NO_UPSTREAM_NOTIFICATION_ID import com.android.networkstack.tethering.TetheringNotificationUpdater.RESTRICTED_NOTIFICATION_ID +import com.android.networkstack.tethering.TetheringNotificationUpdater.ROAMING_NOTIFICATION_ID import com.android.networkstack.tethering.TetheringNotificationUpdater.VERIZON_CARRIER_ID import com.android.testutils.waitForIdle import org.junit.After @@ -75,6 +77,8 @@ const val TEST_MESSAGE = "Tap to set up hotspot." const val TEST_NO_UPSTREAM_TITLE = "Hotspot has no internet access" const val TEST_NO_UPSTREAM_MESSAGE = "Device cannot connect to internet." const val TEST_NO_UPSTREAM_BUTTON = "Turn off hotspot" +const val TEST_ROAMING_TITLE = "Hotspot is on" +const val TEST_ROAMING_MESSAGE = "Additional charges may apply while roaming." @RunWith(AndroidJUnit4::class) @SmallTest @@ -98,6 +102,11 @@ class TetheringNotificationUpdaterTest { "WIFI|BT;android.test:drawable/general", "WIFI|USB;android.test:drawable/general", "USB|BT;android.test:drawable/general", "WIFI|USB|BT;android.test:drawable/general") + private val ROAMING_CAPABILITIES = NetworkCapabilities() + private val HOME_CAPABILITIES = NetworkCapabilities().addCapability(NET_CAPABILITY_NOT_ROAMING) + private val NOTIFICATION_ICON_ID = R.drawable.stat_sys_tether_general + private val TIMEOUT_MS = 500L + private inner class TestContext(c: Context) : BroadcastInterceptingContext(c) { override fun createContextAsUser(user: UserHandle, flags: Int) = if (user == UserHandle.ALL) mockContext else this @@ -123,6 +132,8 @@ class TetheringNotificationUpdaterTest { .getStringArray(R.array.tethering_notification_icons) doReturn(5).`when`(testResources) .getInteger(R.integer.delay_to_show_no_upstream_after_no_backhaul) + doReturn(true).`when`(testResources) + .getBoolean(R.bool.config_upstream_roaming_notification) doReturn(TITLE).`when`(defaultResources).getString(R.string.tethering_notification_title) doReturn(MESSAGE).`when`(defaultResources) .getString(R.string.tethering_notification_message) @@ -135,6 +146,10 @@ class TetheringNotificationUpdaterTest { .getString(R.string.no_upstream_notification_message) doReturn(TEST_NO_UPSTREAM_BUTTON).`when`(testResources) .getString(R.string.no_upstream_notification_disable_button) + doReturn(TEST_ROAMING_TITLE).`when`(testResources) + .getString(R.string.upstream_roaming_notification_title) + doReturn(TEST_ROAMING_MESSAGE).`when`(testResources) + .getString(R.string.upstream_roaming_notification_message) doReturn(USB_ICON_ID).`when`(defaultResources) .getIdentifier(eq("android.test:drawable/usb"), any(), any()) doReturn(BT_ICON_ID).`when`(defaultResources) @@ -176,41 +191,25 @@ class TetheringNotificationUpdaterTest { assertEquals(iconId, notification.smallIcon.resId) assertEquals(title, notification.title()) assertEquals(text, notification.text()) - } - private fun verifyNotificationCancelled(id: Int) = - verify(notificationManager, times(1)).cancel(any(), eq(id)) - - private val tetheringActiveNotifications = - listOf(NO_UPSTREAM_NOTIFICATION_ID, ENABLE_NOTIFICATION_ID) - - private fun verifyCancelAllTetheringActiveNotifications() { - tetheringActiveNotifications.forEach { - verifyNotificationCancelled(it) - } reset(notificationManager) } - private fun verifyOnlyTetheringActiveNotification( - notifyId: Int, - iconId: Int, - title: String, - text: String + private fun verifyNotificationCancelled( + notificationIds: List, + resetAfterVerified: Boolean = true ) { - tetheringActiveNotifications.forEach { - when (it) { - notifyId -> verifyNotification(iconId, title, text, notifyId) - else -> verifyNotificationCancelled(it) - } + notificationIds.forEach { + verify(notificationManager, times(1)).cancel(any(), eq(it)) } - reset(notificationManager) + if (resetAfterVerified) reset(notificationManager) } @Test fun testNotificationWithDownstreamChanged() { // Wifi downstream. No notification. notificationUpdater.onDownstreamChanged(WIFI_MASK) - verifyCancelAllTetheringActiveNotifications() + verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID)) // Same downstream changed. Nothing happened. notificationUpdater.onDownstreamChanged(WIFI_MASK) @@ -218,23 +217,23 @@ class TetheringNotificationUpdaterTest { // Wifi and usb downstreams. Show enable notification notificationUpdater.onDownstreamChanged(WIFI_MASK or USB_MASK) - verifyOnlyTetheringActiveNotification( - ENABLE_NOTIFICATION_ID, GENERAL_ICON_ID, TITLE, MESSAGE) + verifyNotification(GENERAL_ICON_ID, TITLE, MESSAGE, ENABLE_NOTIFICATION_ID) // Usb downstream. Still show enable notification. notificationUpdater.onDownstreamChanged(USB_MASK) - verifyOnlyTetheringActiveNotification(ENABLE_NOTIFICATION_ID, USB_ICON_ID, TITLE, MESSAGE) + verifyNotification(USB_ICON_ID, TITLE, MESSAGE, ENABLE_NOTIFICATION_ID) // No downstream. No notification. notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE) - verifyCancelAllTetheringActiveNotifications() + verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID, + ROAMING_NOTIFICATION_ID)) } @Test fun testNotificationWithActiveDataSubscriptionIdChanged() { // Usb downstream. Showed enable notification with default resource. notificationUpdater.onDownstreamChanged(USB_MASK) - verifyOnlyTetheringActiveNotification(ENABLE_NOTIFICATION_ID, USB_ICON_ID, TITLE, MESSAGE) + verifyNotification(USB_ICON_ID, TITLE, MESSAGE, ENABLE_NOTIFICATION_ID) // Same subId changed. Nothing happened. notificationUpdater.onActiveDataSubscriptionIdChanged(INVALID_SUBSCRIPTION_ID) @@ -242,16 +241,17 @@ class TetheringNotificationUpdaterTest { // Set test sub id. Clear notification with test resource. notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID) - verifyCancelAllTetheringActiveNotifications() + verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID, + ROAMING_NOTIFICATION_ID)) // Wifi downstream. Show enable notification with test resource. notificationUpdater.onDownstreamChanged(WIFI_MASK) - verifyOnlyTetheringActiveNotification( - ENABLE_NOTIFICATION_ID, WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE) + verifyNotification(WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE, ENABLE_NOTIFICATION_ID) // No downstream. No notification. notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE) - verifyCancelAllTetheringActiveNotifications() + verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID, + ROAMING_NOTIFICATION_ID)) } private fun assertIconNumbers(number: Int, configs: Array) { @@ -305,24 +305,21 @@ class TetheringNotificationUpdaterTest { // User restrictions on. Show restricted notification. notificationUpdater.notifyTetheringDisabledByRestriction() - verifyNotification(R.drawable.stat_sys_tether_general, title, message, - RESTRICTED_NOTIFICATION_ID) - reset(notificationManager) + verifyNotification(NOTIFICATION_ICON_ID, title, message, RESTRICTED_NOTIFICATION_ID) // User restrictions off. Clear notification. notificationUpdater.tetheringRestrictionLifted() - verifyNotificationCancelled(RESTRICTED_NOTIFICATION_ID) - reset(notificationManager) + verifyNotificationCancelled(listOf(RESTRICTED_NOTIFICATION_ID)) // Set test sub id. No notification. notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID) - verifyCancelAllTetheringActiveNotifications() + verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID, + ROAMING_NOTIFICATION_ID)) // User restrictions on again. Show restricted notification with test resource. notificationUpdater.notifyTetheringDisabledByRestriction() - verifyNotification(R.drawable.stat_sys_tether_general, disallowTitle, disallowMessage, + verifyNotification(NOTIFICATION_ICON_ID, disallowTitle, disallowMessage, RESTRICTED_NOTIFICATION_ID) - reset(notificationManager) } val MAX_BACKOFF_MS = 200L @@ -359,51 +356,53 @@ class TetheringNotificationUpdaterTest { } @Test - fun testNotificationWithUpstreamNetworkChanged() { + fun testNotificationWithUpstreamCapabilitiesChanged_NoUpstream() { // Set test sub id. No notification. notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID) - verifyCancelAllTetheringActiveNotifications() + verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID, + ROAMING_NOTIFICATION_ID)) // Wifi downstream. Show enable notification with test resource. notificationUpdater.onDownstreamChanged(WIFI_MASK) - verifyOnlyTetheringActiveNotification( - ENABLE_NOTIFICATION_ID, WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE) + verifyNotification(WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE, ENABLE_NOTIFICATION_ID) // There is no upstream. Show no upstream notification. - notificationUpdater.onUpstreamNetworkChanged(null) - notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, 500L) - verifyNotification(R.drawable.stat_sys_tether_general, TEST_NO_UPSTREAM_TITLE, - TEST_NO_UPSTREAM_MESSAGE, NO_UPSTREAM_NOTIFICATION_ID) - reset(notificationManager) + notificationUpdater.onUpstreamCapabilitiesChanged(null) + notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS) + verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE, + NO_UPSTREAM_NOTIFICATION_ID) - // Same upstream network changed. Nothing happened. - notificationUpdater.onUpstreamNetworkChanged(null) + // Same capabilities changed. Nothing happened. + notificationUpdater.onUpstreamCapabilitiesChanged(null) verifyZeroInteractions(notificationManager) // Upstream come back. Clear no upstream notification. - notificationUpdater.onUpstreamNetworkChanged(Network(1000)) - verifyNotificationCancelled(NO_UPSTREAM_NOTIFICATION_ID) - reset(notificationManager) + notificationUpdater.onUpstreamCapabilitiesChanged(HOME_CAPABILITIES) + verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID)) // No upstream again. Show no upstream notification. - notificationUpdater.onUpstreamNetworkChanged(null) - notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, 500L) - verifyNotification(R.drawable.stat_sys_tether_general, TEST_NO_UPSTREAM_TITLE, - TEST_NO_UPSTREAM_MESSAGE, NO_UPSTREAM_NOTIFICATION_ID) - reset(notificationManager) + notificationUpdater.onUpstreamCapabilitiesChanged(null) + notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS) + verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE, + NO_UPSTREAM_NOTIFICATION_ID) // No downstream. No notification. notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE) - verifyCancelAllTetheringActiveNotifications() + verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID, + ROAMING_NOTIFICATION_ID)) - // Set R.integer.delay_to_show_no_upstream_after_no_backhaul to 0 and have wifi downstream - // again. Show enable notification only. + // Put up enable notification with wifi downstream and home capabilities. + notificationUpdater.onDownstreamChanged(WIFI_MASK) + notificationUpdater.onUpstreamCapabilitiesChanged(HOME_CAPABILITIES) + verifyNotification(WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE, ENABLE_NOTIFICATION_ID) + + // Set R.integer.delay_to_show_no_upstream_after_no_backhaul to -1 and change to no upstream + // again. Don't put up no upstream notification. doReturn(-1).`when`(testResources) .getInteger(R.integer.delay_to_show_no_upstream_after_no_backhaul) - notificationUpdater.onDownstreamChanged(WIFI_MASK) - notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, 500L) - verifyOnlyTetheringActiveNotification( - ENABLE_NOTIFICATION_ID, WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE) + notificationUpdater.onUpstreamCapabilitiesChanged(null) + notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS) + verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID)) } @Test @@ -428,4 +427,57 @@ class TetheringNotificationUpdaterTest { assertEquals(311, res.configuration.mcc) assertEquals(480, res.configuration.mnc) } + + @Test + fun testNotificationWithUpstreamCapabilitiesChanged_Roaming() { + // Set test sub id. Clear notification. + notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID) + verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID, + ROAMING_NOTIFICATION_ID)) + + // Wifi downstream. Show enable notification with test resource. + notificationUpdater.onDownstreamChanged(WIFI_MASK) + verifyNotification(WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE, ENABLE_NOTIFICATION_ID) + + // Upstream capabilities changed to roaming state. Show roaming notification. + notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES) + verifyNotification(NOTIFICATION_ICON_ID, TEST_ROAMING_TITLE, TEST_ROAMING_MESSAGE, + ROAMING_NOTIFICATION_ID) + + // Same capabilities change. Nothing happened. + notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES) + verifyZeroInteractions(notificationManager) + + // Upstream capabilities changed to home state. Clear roaming notification. + notificationUpdater.onUpstreamCapabilitiesChanged(HOME_CAPABILITIES) + verifyNotificationCancelled(listOf(ROAMING_NOTIFICATION_ID)) + + // Upstream capabilities changed to roaming state again. Show roaming notification. + notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES) + verifyNotification(NOTIFICATION_ICON_ID, TEST_ROAMING_TITLE, TEST_ROAMING_MESSAGE, + ROAMING_NOTIFICATION_ID) + + // No upstream. Clear roaming notification and show no upstream notification. + notificationUpdater.onUpstreamCapabilitiesChanged(null) + notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS) + verifyNotificationCancelled(listOf(ROAMING_NOTIFICATION_ID), false) + verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE, + NO_UPSTREAM_NOTIFICATION_ID) + + // No downstream. No notification. + notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE) + verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID, + ROAMING_NOTIFICATION_ID)) + + // Wifi downstream again. Show enable notification with test resource. + notificationUpdater.onDownstreamChanged(WIFI_MASK) + verifyNotification(WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE, ENABLE_NOTIFICATION_ID) + + // Set R.bool.config_upstream_roaming_notification to false and change upstream + // network to roaming state again. No roaming notification. + doReturn(false).`when`(testResources) + .getBoolean(R.bool.config_upstream_roaming_notification) + notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES) + verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID)) + } } 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 feb99e6b24..cf0548304a 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java @@ -48,6 +48,7 @@ import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import static com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE; +import static com.android.networkstack.tethering.UpstreamNetworkMonitor.EVENT_ON_CAPABILITIES; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -1700,7 +1701,29 @@ public class TetheringTest { stateMachine.chooseUpstreamType(true); verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(eq(upstreamState.network)); - verify(mNotificationUpdater, times(1)).onUpstreamNetworkChanged(eq(upstreamState.network)); + verify(mNotificationUpdater, times(1)).onUpstreamCapabilitiesChanged(any()); + } + + @Test + public void testUpstreamCapabilitiesChanged() { + final Tethering.TetherMasterSM stateMachine = (Tethering.TetherMasterSM) + mTetheringDependencies.mUpstreamNetworkMonitorMasterSM; + final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState(); + when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())).thenReturn(upstreamState); + stateMachine.chooseUpstreamType(true); + + stateMachine.handleUpstreamNetworkMonitorCallback(EVENT_ON_CAPABILITIES, upstreamState); + // Should have two onUpstreamCapabilitiesChanged(). + // One is called by reportUpstreamChanged(). One is called by EVENT_ON_CAPABILITIES. + verify(mNotificationUpdater, times(2)).onUpstreamCapabilitiesChanged(any()); + reset(mNotificationUpdater); + + // Verify that onUpstreamCapabilitiesChanged won't be called if not current upstream network + // capabilities changed. + final UpstreamNetworkState upstreamState2 = new UpstreamNetworkState( + upstreamState.linkProperties, upstreamState.networkCapabilities, new Network(101)); + stateMachine.handleUpstreamNetworkMonitorCallback(EVENT_ON_CAPABILITIES, upstreamState2); + verify(mNotificationUpdater, never()).onUpstreamCapabilitiesChanged(any()); } // TODO: Test that a request for hotspot mode doesn't interfere with an From ef3415990999e8249d4d0676f4c369f3750dafde Mon Sep 17 00:00:00 2001 From: Paul Hu Date: Wed, 22 Apr 2020 04:05:26 +0000 Subject: [PATCH 105/188] [TNU07] Remove unused resources Some strings are not used, so just remove them. Bug: 154445061 Test: atest TetheringTests Change-Id: Ie67f7f4d0863d58c4c5206e445c1dbdfe3f65f60 Merged-In: Ie67f7f4d0863d58c4c5206e445c1dbdfe3f65f60 (cherry picked from commit 59eeedc045aaaa0c7d5b97277d25f64cce254464, aosp/1289100) --- .../res/values-mcc310-mnc004/strings.xml | 2 -- .../res/values-mcc310-mnc120/strings.xml | 22 ------------------- .../res/values-mcc311-mnc480/strings.xml | 2 -- .../res/values-mcc311-mnc490/strings.xml | 22 ------------------- .../res/values-mcc312-mnc530/strings.xml | 22 ------------------- Tethering/res/values/strings.xml | 5 ----- 6 files changed, 75 deletions(-) delete mode 100644 Tethering/res/values-mcc310-mnc120/strings.xml delete mode 100644 Tethering/res/values-mcc311-mnc490/strings.xml delete mode 100644 Tethering/res/values-mcc312-mnc530/strings.xml diff --git a/Tethering/res/values-mcc310-mnc004/strings.xml b/Tethering/res/values-mcc310-mnc004/strings.xml index 9dadd49cf8..ce9ff60807 100644 --- a/Tethering/res/values-mcc310-mnc004/strings.xml +++ b/Tethering/res/values-mcc310-mnc004/strings.xml @@ -25,6 +25,4 @@ Hotspot or tethering is on Additional charges may apply while roaming - - Continue diff --git a/Tethering/res/values-mcc310-mnc120/strings.xml b/Tethering/res/values-mcc310-mnc120/strings.xml deleted file mode 100644 index 618df90c71..0000000000 --- a/Tethering/res/values-mcc310-mnc120/strings.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - %1$d device connected. - %1$d devices connected. - - \ No newline at end of file diff --git a/Tethering/res/values-mcc311-mnc480/strings.xml b/Tethering/res/values-mcc311-mnc480/strings.xml index 9dadd49cf8..ce9ff60807 100644 --- a/Tethering/res/values-mcc311-mnc480/strings.xml +++ b/Tethering/res/values-mcc311-mnc480/strings.xml @@ -25,6 +25,4 @@ Hotspot or tethering is on Additional charges may apply while roaming - - Continue diff --git a/Tethering/res/values-mcc311-mnc490/strings.xml b/Tethering/res/values-mcc311-mnc490/strings.xml deleted file mode 100644 index 618df90c71..0000000000 --- a/Tethering/res/values-mcc311-mnc490/strings.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - %1$d device connected. - %1$d devices connected. - - \ No newline at end of file diff --git a/Tethering/res/values-mcc312-mnc530/strings.xml b/Tethering/res/values-mcc312-mnc530/strings.xml deleted file mode 100644 index 618df90c71..0000000000 --- a/Tethering/res/values-mcc312-mnc530/strings.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - %1$d device connected. - %1$d devices connected. - - \ No newline at end of file diff --git a/Tethering/res/values/strings.xml b/Tethering/res/values/strings.xml index 4fa60d4125..d63c7c5063 100644 --- a/Tethering/res/values/strings.xml +++ b/Tethering/res/values/strings.xml @@ -19,9 +19,6 @@ Tethering or hotspot active Tap to set up. - - - - - From 83a814eb3192917cb83a34db4e35a295a442edb9 Mon Sep 17 00:00:00 2001 From: Treehugger Robot Date: Tue, 21 Apr 2020 15:16:43 +0000 Subject: [PATCH 106/188] Retry the call that fetches the tethering offload HAL. The CL that moved the initialization of the tethering offload config HAL from C++ to Java caused the code not to retry fetching the service if it is not ready when tethering is started. This is because the C++ version of getService() retries, but the Java version only retries if getService(true) is called. Make the new code retry as well. b/152430668#comment4 asserts that the fetch will be retried only if the service is installed on the device, so the retries should be attempted (and thus should not have any startup time impact) on devices that do not support tethering offload. Bug: 152430668 Test: builds, boots, tethering offload works Merged-In: I093f127d90b2aa1b13eb0748378a24726d419472 Change-Id: I093f127d90b2aa1b13eb0748378a24726d419472 --- .../networkstack/tethering/OffloadHardwareInterface.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java b/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java index 85a23fb83f..55344fc75d 100644 --- a/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java +++ b/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java @@ -142,7 +142,7 @@ public class OffloadHardwareInterface { public boolean initOffloadConfig() { IOffloadConfig offloadConfig; try { - offloadConfig = IOffloadConfig.getService(); + offloadConfig = IOffloadConfig.getService(true /*retry*/); } catch (RemoteException e) { mLog.e("getIOffloadConfig error " + e); return false; From 127ee5920f06d9c887045712db9b2f00ceefba03 Mon Sep 17 00:00:00 2001 From: Paul Hu Date: Wed, 22 Apr 2020 06:37:51 +0000 Subject: [PATCH 107/188] [TNU08] Remove the "tethering is on" notification All carriers discarded the requirement to put up a standing notification when tethering is on. Thus, remove the "tethering is on" notification. Bug: 154438388 Test: atest TetheringTests Change-Id: Ife3915837b6b7b83d3eaaa84b71b6409ff37b71c Merged-In: Ife3915837b6b7b83d3eaaa84b71b6409ff37b71c (cherry picked from commit 0171c07d05dd2625c6dcfd47977a701ddc2d5d36, aosp/1289107) --- Tethering/res/values/config.xml | 45 ---- Tethering/res/values/overlayable.xml | 38 ---- .../TetheringNotificationUpdater.java | 100 --------- .../TetheringNotificationUpdaterTest.kt | 207 ++++-------------- 4 files changed, 39 insertions(+), 351 deletions(-) diff --git a/Tethering/res/values/config.xml b/Tethering/res/values/config.xml index 66fbefca94..83c99d22fd 100644 --- a/Tethering/res/values/config.xml +++ b/Tethering/res/values/config.xml @@ -158,51 +158,6 @@ com.android.settings/.wifi.tether.TetherService - - - - - USB;com.android.networkstack.tethering:drawable/stat_sys_tether_usb - BT;com.android.networkstack.tethering:drawable/stat_sys_tether_bluetooth - WIFI|USB,WIFI|BT,USB|BT,WIFI|USB|BT;com.android.networkstack.tethering:drawable/stat_sys_tether_general - - - @string/tethered_notification_title - - @string/tethered_notification_message - - - - - - - diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java b/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java index f490cc4719..7fd6b61ccd 100644 --- a/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java @@ -17,9 +17,6 @@ package com.android.networkstack.tethering; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; -import static android.net.TetheringManager.TETHERING_BLUETOOTH; -import static android.net.TetheringManager.TETHERING_USB; -import static android.net.TetheringManager.TETHERING_WIFI; import static android.text.TextUtils.isEmpty; import android.app.Notification; @@ -39,10 +36,8 @@ import android.os.UserHandle; import android.provider.Settings; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; -import android.util.Log; import android.util.SparseArray; -import androidx.annotation.ArrayRes; import androidx.annotation.DrawableRes; import androidx.annotation.IntDef; import androidx.annotation.IntRange; @@ -77,9 +72,6 @@ public class TetheringNotificationUpdater { private static final boolean NO_NOTIFY = false; @VisibleForTesting static final int EVENT_SHOW_NO_UPSTREAM = 1; - // Id to update and cancel enable notification. Must be unique within the tethering app. - @VisibleForTesting - static final int ENABLE_NOTIFICATION_ID = 1000; // Id to update and cancel restricted notification. Must be unique within the tethering app. @VisibleForTesting static final int RESTRICTED_NOTIFICATION_ID = 1001; @@ -120,7 +112,6 @@ public class TetheringNotificationUpdater { @Retention(RetentionPolicy.SOURCE) @IntDef(value = { - ENABLE_NOTIFICATION_ID, RESTRICTED_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID @@ -223,7 +214,6 @@ public class TetheringNotificationUpdater { final boolean tetheringActiveChanged = (downstreamTypes == DOWNSTREAM_NONE) != (mDownstreamTypesMask == DOWNSTREAM_NONE); final boolean subIdChanged = subId != mActiveDataSubId; - final boolean downstreamChanged = downstreamTypes != mDownstreamTypesMask; final boolean upstreamChanged = noUpstream != mNoUpstream; final boolean roamingChanged = isRoaming != mRoaming; final boolean updateAll = tetheringActiveChanged || subIdChanged; @@ -232,19 +222,10 @@ public class TetheringNotificationUpdater { mNoUpstream = noUpstream; mRoaming = isRoaming; - if (updateAll || downstreamChanged) updateEnableNotification(); if (updateAll || upstreamChanged) updateNoUpstreamNotification(); if (updateAll || roamingChanged) updateRoamingNotification(); } - private void updateEnableNotification() { - final boolean tetheringInactive = mDownstreamTypesMask == DOWNSTREAM_NONE; - - if (tetheringInactive || setupNotification() == NO_NOTIFY) { - clearNotification(ENABLE_NOTIFICATION_ID); - } - } - private void updateNoUpstreamNotification() { final boolean tetheringInactive = mDownstreamTypesMask == DOWNSTREAM_NONE; @@ -310,64 +291,6 @@ public class TetheringNotificationUpdater { NO_UPSTREAM_NOTIFICATION_ID, null /* pendingIntent */, action); } - /** - * Returns the downstream types mask which convert from given string. - * - * @param types This string has to be made by "WIFI", "USB", "BT", and OR'd with the others. - * - * @return downstream types mask value. - */ - @VisibleForTesting - @IntRange(from = 0, to = 7) - int getDownstreamTypesMask(@NonNull final String types) { - int downstreamTypesMask = DOWNSTREAM_NONE; - final String[] downstreams = types.split("\\|"); - for (String downstream : downstreams) { - if (USB_DOWNSTREAM.equals(downstream.trim())) { - downstreamTypesMask |= (1 << TETHERING_USB); - } else if (WIFI_DOWNSTREAM.equals(downstream.trim())) { - downstreamTypesMask |= (1 << TETHERING_WIFI); - } else if (BLUETOOTH_DOWNSTREAM.equals(downstream.trim())) { - downstreamTypesMask |= (1 << TETHERING_BLUETOOTH); - } - } - return downstreamTypesMask; - } - - /** - * Returns the icons {@link android.util.SparseArray} which get from given string-array resource - * id. - * - * @param id String-array resource id - * - * @return {@link android.util.SparseArray} with downstream types and icon id info. - */ - @NonNull - @VisibleForTesting - SparseArray getIcons(@ArrayRes int id, @NonNull Resources res) { - final String[] array = res.getStringArray(id); - final SparseArray icons = new SparseArray<>(); - for (String config : array) { - if (isEmpty(config)) continue; - - final String[] elements = config.split(";"); - if (elements.length != 2) { - Log.wtf(TAG, - "Unexpected format in Tethering notification configuration : " + config); - continue; - } - - final String[] types = elements[0].split(","); - for (String type : types) { - int mask = getDownstreamTypesMask(type); - if (mask == DOWNSTREAM_NONE) continue; - icons.put(mask, res.getIdentifier( - elements[1].trim(), null /* defType */, null /* defPackage */)); - } - } - return icons; - } - private boolean setupRoamingNotification() { final Resources res = getResourcesForSubId(mContext, mActiveDataSubId); final boolean upstreamRoamingNotification = @@ -403,29 +326,6 @@ public class TetheringNotificationUpdater { return NOTIFY_DONE; } - private boolean setupNotification() { - final Resources res = getResourcesForSubId(mContext, mActiveDataSubId); - final SparseArray downstreamIcons = - getIcons(R.array.tethering_notification_icons, res); - - final int iconId = downstreamIcons.get(mDownstreamTypesMask, NO_ICON_ID); - if (iconId == NO_ICON_ID) return NO_NOTIFY; - - final String title = res.getString(R.string.tethering_notification_title); - final String message = res.getString(R.string.tethering_notification_message); - if (isEmpty(title) || isEmpty(message)) return NO_NOTIFY; - - final PendingIntent pi = PendingIntent.getActivity( - mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */), - 0 /* requestCode */, - new Intent(Settings.ACTION_TETHER_SETTINGS), - Intent.FLAG_ACTIVITY_NEW_TASK, - null /* options */); - - showNotification(iconId, title, message, ENABLE_NOTIFICATION_ID, pi, new Action[0]); - return NOTIFY_DONE; - } - private void showNotification(@DrawableRes final int iconId, @NonNull final String title, @NonNull final String message, @NotificationId final int id, @Nullable PendingIntent pi, @NonNull final Action... actions) { diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt index 04f31a7a28..745468fdf3 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt @@ -20,8 +20,6 @@ import android.app.Notification import android.app.NotificationManager import android.content.Context import android.content.res.Resources -import android.net.ConnectivityManager.TETHERING_BLUETOOTH -import android.net.ConnectivityManager.TETHERING_USB import android.net.ConnectivityManager.TETHERING_WIFI import android.os.Handler import android.os.HandlerThread @@ -29,14 +27,12 @@ import android.os.Looper import android.net.NetworkCapabilities import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING import android.os.UserHandle -import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID import android.telephony.TelephonyManager import androidx.test.filters.SmallTest import androidx.test.platform.app.InstrumentationRegistry import androidx.test.runner.AndroidJUnit4 import com.android.internal.util.test.BroadcastInterceptingContext import com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE -import com.android.networkstack.tethering.TetheringNotificationUpdater.ENABLE_NOTIFICATION_ID import com.android.networkstack.tethering.TetheringNotificationUpdater.EVENT_SHOW_NO_UPSTREAM import com.android.networkstack.tethering.TetheringNotificationUpdater.NO_UPSTREAM_NOTIFICATION_ID import com.android.networkstack.tethering.TetheringNotificationUpdater.RESTRICTED_NOTIFICATION_ID @@ -63,17 +59,9 @@ import org.mockito.Mockito.verifyZeroInteractions import org.mockito.MockitoAnnotations const val TEST_SUBID = 1 -const val WIFI_ICON_ID = 1 -const val USB_ICON_ID = 2 -const val BT_ICON_ID = 3 -const val GENERAL_ICON_ID = 4 const val WIFI_MASK = 1 shl TETHERING_WIFI -const val USB_MASK = 1 shl TETHERING_USB -const val BT_MASK = 1 shl TETHERING_BLUETOOTH -const val TITLE = "Tethering active" -const val MESSAGE = "Tap here to set up." -const val TEST_TITLE = "Hotspot active" -const val TEST_MESSAGE = "Tap to set up hotspot." +const val TEST_DISALLOW_TITLE = "Tether function is disallowed" +const val TEST_DISALLOW_MESSAGE = "Please contact your admin" const val TEST_NO_UPSTREAM_TITLE = "Hotspot has no internet access" const val TEST_NO_UPSTREAM_MESSAGE = "Device cannot connect to internet." const val TEST_NO_UPSTREAM_BUTTON = "Turn off hotspot" @@ -88,7 +76,6 @@ class TetheringNotificationUpdaterTest { @Mock private lateinit var mockContext: Context @Mock private lateinit var notificationManager: NotificationManager @Mock private lateinit var telephonyManager: TelephonyManager - @Mock private lateinit var defaultResources: Resources @Mock private lateinit var testResources: Resources // lateinit for these classes under test, as they should be reset to a different instance for @@ -97,11 +84,6 @@ class TetheringNotificationUpdaterTest { private lateinit var notificationUpdater: TetheringNotificationUpdater private lateinit var fakeTetheringThread: HandlerThread - private val ENABLE_ICON_CONFIGS = arrayOf( - "USB;android.test:drawable/usb", "BT;android.test:drawable/bluetooth", - "WIFI|BT;android.test:drawable/general", "WIFI|USB;android.test:drawable/general", - "USB|BT;android.test:drawable/general", "WIFI|USB|BT;android.test:drawable/general") - private val ROAMING_CAPABILITIES = NetworkCapabilities() private val HOME_CAPABILITIES = NetworkCapabilities().addCapability(NET_CAPABILITY_NOT_ROAMING) private val NOTIFICATION_ICON_ID = R.drawable.stat_sys_tether_general @@ -117,29 +99,19 @@ class TetheringNotificationUpdaterTest { private inner class WrappedNotificationUpdater(c: Context, looper: Looper) : TetheringNotificationUpdater(c, looper) { - override fun getResourcesForSubId(context: Context, subId: Int) = - when (subId) { - TEST_SUBID -> testResources - INVALID_SUBSCRIPTION_ID -> defaultResources - else -> super.getResourcesForSubId(context, subId) - } + override fun getResourcesForSubId(c: Context, subId: Int) = + if (subId == TEST_SUBID) testResources else super.getResourcesForSubId(c, subId) } private fun setupResources() { - doReturn(ENABLE_ICON_CONFIGS).`when`(defaultResources) - .getStringArray(R.array.tethering_notification_icons) - doReturn(arrayOf("WIFI;android.test:drawable/wifi")).`when`(testResources) - .getStringArray(R.array.tethering_notification_icons) doReturn(5).`when`(testResources) .getInteger(R.integer.delay_to_show_no_upstream_after_no_backhaul) doReturn(true).`when`(testResources) .getBoolean(R.bool.config_upstream_roaming_notification) - doReturn(TITLE).`when`(defaultResources).getString(R.string.tethering_notification_title) - doReturn(MESSAGE).`when`(defaultResources) - .getString(R.string.tethering_notification_message) - doReturn(TEST_TITLE).`when`(testResources).getString(R.string.tethering_notification_title) - doReturn(TEST_MESSAGE).`when`(testResources) - .getString(R.string.tethering_notification_message) + doReturn(TEST_DISALLOW_TITLE).`when`(testResources) + .getString(R.string.disable_tether_notification_title) + doReturn(TEST_DISALLOW_MESSAGE).`when`(testResources) + .getString(R.string.disable_tether_notification_message) doReturn(TEST_NO_UPSTREAM_TITLE).`when`(testResources) .getString(R.string.no_upstream_notification_title) doReturn(TEST_NO_UPSTREAM_MESSAGE).`when`(testResources) @@ -150,14 +122,6 @@ class TetheringNotificationUpdaterTest { .getString(R.string.upstream_roaming_notification_title) doReturn(TEST_ROAMING_MESSAGE).`when`(testResources) .getString(R.string.upstream_roaming_notification_message) - doReturn(USB_ICON_ID).`when`(defaultResources) - .getIdentifier(eq("android.test:drawable/usb"), any(), any()) - doReturn(BT_ICON_ID).`when`(defaultResources) - .getIdentifier(eq("android.test:drawable/bluetooth"), any(), any()) - doReturn(GENERAL_ICON_ID).`when`(defaultResources) - .getIdentifier(eq("android.test:drawable/general"), any(), any()) - doReturn(WIFI_ICON_ID).`when`(testResources) - .getIdentifier(eq("android.test:drawable/wifi"), any(), any()) } @Before @@ -206,119 +170,27 @@ class TetheringNotificationUpdaterTest { } @Test - fun testNotificationWithDownstreamChanged() { - // Wifi downstream. No notification. - notificationUpdater.onDownstreamChanged(WIFI_MASK) - verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID)) - - // Same downstream changed. Nothing happened. - notificationUpdater.onDownstreamChanged(WIFI_MASK) - verifyZeroInteractions(notificationManager) - - // Wifi and usb downstreams. Show enable notification - notificationUpdater.onDownstreamChanged(WIFI_MASK or USB_MASK) - verifyNotification(GENERAL_ICON_ID, TITLE, MESSAGE, ENABLE_NOTIFICATION_ID) - - // Usb downstream. Still show enable notification. - notificationUpdater.onDownstreamChanged(USB_MASK) - verifyNotification(USB_ICON_ID, TITLE, MESSAGE, ENABLE_NOTIFICATION_ID) - - // No downstream. No notification. - notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE) - verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID, - ROAMING_NOTIFICATION_ID)) - } - - @Test - fun testNotificationWithActiveDataSubscriptionIdChanged() { - // Usb downstream. Showed enable notification with default resource. - notificationUpdater.onDownstreamChanged(USB_MASK) - verifyNotification(USB_ICON_ID, TITLE, MESSAGE, ENABLE_NOTIFICATION_ID) - - // Same subId changed. Nothing happened. - notificationUpdater.onActiveDataSubscriptionIdChanged(INVALID_SUBSCRIPTION_ID) - verifyZeroInteractions(notificationManager) - - // Set test sub id. Clear notification with test resource. + fun testRestrictedNotification() { + // Set test sub id. notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID) - verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID, - ROAMING_NOTIFICATION_ID)) - - // Wifi downstream. Show enable notification with test resource. - notificationUpdater.onDownstreamChanged(WIFI_MASK) - verifyNotification(WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE, ENABLE_NOTIFICATION_ID) - - // No downstream. No notification. - notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE) - verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID, - ROAMING_NOTIFICATION_ID)) - } - - private fun assertIconNumbers(number: Int, configs: Array) { - doReturn(configs).`when`(defaultResources) - .getStringArray(R.array.tethering_notification_icons) - assertEquals(number, notificationUpdater.getIcons( - R.array.tethering_notification_icons, defaultResources).size()) - } - - @Test - fun testGetIcons() { - assertIconNumbers(0, arrayOfNulls(0)) - assertIconNumbers(0, arrayOf(null, "")) - assertIconNumbers(3, arrayOf( - // These configurations are invalid with wrong strings or symbols. - ";", ",", "|", "|,;", "WIFI", "1;2", " U SB; ", "bt;", "WIFI;USB;BT", "WIFI|USB|BT", - "WIFI,BT,USB", " WIFI| | | USB, test:drawable/test", - // This configuration is valid with two downstream types (USB, BT). - "USB|,,,,,|BT;drawable/test ", - // This configuration is valid with one downstream types (WIFI). - " WIFI ; android.test:drawable/xxx ")) - } - - @Test - fun testGetDownstreamTypesMask() { - assertEquals(DOWNSTREAM_NONE, notificationUpdater.getDownstreamTypesMask("")) - assertEquals(DOWNSTREAM_NONE, notificationUpdater.getDownstreamTypesMask("1")) - assertEquals(DOWNSTREAM_NONE, notificationUpdater.getDownstreamTypesMask("WIFI_P2P")) - assertEquals(DOWNSTREAM_NONE, notificationUpdater.getDownstreamTypesMask("usb")) - assertEquals(WIFI_MASK, notificationUpdater.getDownstreamTypesMask(" WIFI ")) - assertEquals(USB_MASK, notificationUpdater.getDownstreamTypesMask("USB | B T")) - assertEquals(BT_MASK, notificationUpdater.getDownstreamTypesMask(" WIFI: | BT")) - assertEquals(WIFI_MASK or USB_MASK, - notificationUpdater.getDownstreamTypesMask("1|2|USB|WIFI|BLUETOOTH||")) - } - - @Test - fun testSetupRestrictedNotification() { - val title = context.resources.getString(R.string.disable_tether_notification_title) - val message = context.resources.getString(R.string.disable_tether_notification_message) - val disallowTitle = "Tether function is disallowed" - val disallowMessage = "Please contact your admin" - doReturn(title).`when`(defaultResources) - .getString(R.string.disable_tether_notification_title) - doReturn(message).`when`(defaultResources) - .getString(R.string.disable_tether_notification_message) - doReturn(disallowTitle).`when`(testResources) - .getString(R.string.disable_tether_notification_title) - doReturn(disallowMessage).`when`(testResources) - .getString(R.string.disable_tether_notification_message) + verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID)) // User restrictions on. Show restricted notification. notificationUpdater.notifyTetheringDisabledByRestriction() - verifyNotification(NOTIFICATION_ICON_ID, title, message, RESTRICTED_NOTIFICATION_ID) + verifyNotification(NOTIFICATION_ICON_ID, TEST_DISALLOW_TITLE, TEST_DISALLOW_MESSAGE, + RESTRICTED_NOTIFICATION_ID) // User restrictions off. Clear notification. notificationUpdater.tetheringRestrictionLifted() verifyNotificationCancelled(listOf(RESTRICTED_NOTIFICATION_ID)) - // Set test sub id. No notification. - notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID) - verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID, - ROAMING_NOTIFICATION_ID)) + // No downstream. + notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE) + verifyZeroInteractions(notificationManager) - // User restrictions on again. Show restricted notification with test resource. + // User restrictions on again. Show restricted notification. notificationUpdater.notifyTetheringDisabledByRestriction() - verifyNotification(NOTIFICATION_ICON_ID, disallowTitle, disallowMessage, + verifyNotification(NOTIFICATION_ICON_ID, TEST_DISALLOW_TITLE, TEST_DISALLOW_MESSAGE, RESTRICTED_NOTIFICATION_ID) } @@ -356,15 +228,14 @@ class TetheringNotificationUpdaterTest { } @Test - fun testNotificationWithUpstreamCapabilitiesChanged_NoUpstream() { - // Set test sub id. No notification. + fun testNoUpstreamNotification() { + // Set test sub id. notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID) - verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID, - ROAMING_NOTIFICATION_ID)) + verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID)) - // Wifi downstream. Show enable notification with test resource. + // Wifi downstream. notificationUpdater.onDownstreamChanged(WIFI_MASK) - verifyNotification(WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE, ENABLE_NOTIFICATION_ID) + verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID)) // There is no upstream. Show no upstream notification. notificationUpdater.onUpstreamCapabilitiesChanged(null) @@ -386,15 +257,14 @@ class TetheringNotificationUpdaterTest { verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE, NO_UPSTREAM_NOTIFICATION_ID) - // No downstream. No notification. + // No downstream. notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE) - verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID, - ROAMING_NOTIFICATION_ID)) + verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID)) - // Put up enable notification with wifi downstream and home capabilities. + // Wifi downstream and home capabilities. notificationUpdater.onDownstreamChanged(WIFI_MASK) notificationUpdater.onUpstreamCapabilitiesChanged(HOME_CAPABILITIES) - verifyNotification(WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE, ENABLE_NOTIFICATION_ID) + verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID)) // Set R.integer.delay_to_show_no_upstream_after_no_backhaul to -1 and change to no upstream // again. Don't put up no upstream notification. @@ -429,15 +299,14 @@ class TetheringNotificationUpdaterTest { } @Test - fun testNotificationWithUpstreamCapabilitiesChanged_Roaming() { - // Set test sub id. Clear notification. + fun testRoamingNotification() { + // Set test sub id. notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID) - verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID, - ROAMING_NOTIFICATION_ID)) + verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID)) - // Wifi downstream. Show enable notification with test resource. + // Wifi downstream. notificationUpdater.onDownstreamChanged(WIFI_MASK) - verifyNotification(WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE, ENABLE_NOTIFICATION_ID) + verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID)) // Upstream capabilities changed to roaming state. Show roaming notification. notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES) @@ -464,14 +333,16 @@ class TetheringNotificationUpdaterTest { verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE, NO_UPSTREAM_NOTIFICATION_ID) - // No downstream. No notification. + // No downstream. notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE) - verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID, - ROAMING_NOTIFICATION_ID)) + verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID)) - // Wifi downstream again. Show enable notification with test resource. + // Wifi downstream again. notificationUpdater.onDownstreamChanged(WIFI_MASK) - verifyNotification(WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE, ENABLE_NOTIFICATION_ID) + notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS) + verifyNotificationCancelled(listOf(ROAMING_NOTIFICATION_ID), false) + verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE, + NO_UPSTREAM_NOTIFICATION_ID) // Set R.bool.config_upstream_roaming_notification to false and change upstream // network to roaming state again. No roaming notification. From d0ed7dd8f95e1b29eb8e8d2073a22380f1a40777 Mon Sep 17 00:00:00 2001 From: Mark Chien Date: Wed, 22 Apr 2020 09:23:04 +0000 Subject: [PATCH 108/188] Include NetworkStaticLibTestsLib into tethering coverage test Bug: 148636687 Test: atest NetworkStaticLibTests Test: atest TetheringCoverageTests Merged-In: I8cd9dbc9fe163583ff1d016c9262546949105b80 Change-Id: I8cd9dbc9fe163583ff1d016c9262546949105b80 --- Tethering/tests/integration/Android.bp | 1 + 1 file changed, 1 insertion(+) diff --git a/Tethering/tests/integration/Android.bp b/Tethering/tests/integration/Android.bp index 6b751afdf5..3305ed0844 100644 --- a/Tethering/tests/integration/Android.bp +++ b/Tethering/tests/integration/Android.bp @@ -69,6 +69,7 @@ android_test { test_config: "AndroidTest_Coverage.xml", defaults: ["libnetworkstackutilsjni_deps"], static_libs: [ + "NetworkStaticLibTestsLib", "NetworkStackTestsLib", "TetheringTestsLib", "TetheringIntegrationTestsLib", From 1cae88f5602c793557abe643dca1bd6fc800dbbd Mon Sep 17 00:00:00 2001 From: Paul Hu Date: Wed, 22 Apr 2020 14:01:56 +0000 Subject: [PATCH 109/188] Add TetheringCommonTests Bug: 153614365 Bug: 153613717 Test: atest TetheringTests TetheringCoverageTests Change-Id: If7c933ec0c72943312cd37bfc66918f10a5504a9 Merged-In: If7c933ec0c72943312cd37bfc66918f10a5504a9 (cherry picked from commit f340f6fd46b4c37f9628608c2a8e02529f6692d1, aosp/1290553) --- Tethering/tests/unit/Android.bp | 21 +++++++++++++++++++ .../android/net/TetheredClientTest.kt | 0 2 files changed, 21 insertions(+) rename Tethering/tests/unit/{src => common}/android/net/TetheredClientTest.kt (100%) diff --git a/Tethering/tests/unit/Android.bp b/Tethering/tests/unit/Android.bp index 26517ceb72..08cfb30813 100644 --- a/Tethering/tests/unit/Android.bp +++ b/Tethering/tests/unit/Android.bp @@ -14,6 +14,26 @@ // limitations under the License. // +// Tests in this folder are included both in unit tests and CTS. +java_library { + name: "TetheringCommonTests", + srcs: [ + "common/**/*.java", + "common/**/*.kt" + ], + static_libs: [ + "androidx.test.rules", + "net-tests-utils", + ], + // TODO(b/147200698) change sdk_version to module-current and remove framework-minus-apex + sdk_version: "core_platform", + libs: [ + "framework-minus-apex", + "framework-tethering", + ], + visibility: ["//cts/tests/tests/tethering"], +} + java_defaults { name: "TetheringTestsDefaults", srcs: [ @@ -22,6 +42,7 @@ java_defaults { ], static_libs: [ "TetheringApiCurrentLib", + "TetheringCommonTests", "androidx.test.rules", "frameworks-base-testutils", "mockito-target-extended-minus-junit4", diff --git a/Tethering/tests/unit/src/android/net/TetheredClientTest.kt b/Tethering/tests/unit/common/android/net/TetheredClientTest.kt similarity index 100% rename from Tethering/tests/unit/src/android/net/TetheredClientTest.kt rename to Tethering/tests/unit/common/android/net/TetheredClientTest.kt From 70521ef0e809d24d3e03b78b79e659c0ee6ca963 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Wed, 22 Apr 2020 15:24:12 -0700 Subject: [PATCH 110/188] Import translations. DO NOT MERGE Change-Id: I4b3a8cf8d738bf6f680258f1ebd77d894dc986cd Auto-generated-cl: translation import --- Tethering/res/values-af/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-am/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-ar/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-as/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-az/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-b+sr+Latn/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-be/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-bg/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-bn/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-bs/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-ca/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-cs/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-da/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-de/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-el/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-en-rAU/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-en-rCA/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-en-rGB/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-en-rIN/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-en-rXC/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-es-rUS/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-es/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-et/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-eu/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-fa/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-fi/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-fr-rCA/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-fr/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-gl/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-gu/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-hi/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-hr/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-hu/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-hy/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-in/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-is/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-it/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-iw/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-ja/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-ka/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-kk/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-km/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-kn/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-ko/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-ky/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-lo/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-lt/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-lv/strings.xml | 29 ++++++++++++++++--- .../res/values-mcc310-mnc004-af/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-am/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-ar/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-as/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-az/strings.xml | 24 +++++++++++++++ .../strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-be/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-bg/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-bn/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-bs/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-ca/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-cs/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-da/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-de/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-el/strings.xml | 24 +++++++++++++++ .../values-mcc310-mnc004-en-rAU/strings.xml | 24 +++++++++++++++ .../values-mcc310-mnc004-en-rCA/strings.xml | 24 +++++++++++++++ .../values-mcc310-mnc004-en-rGB/strings.xml | 24 +++++++++++++++ .../values-mcc310-mnc004-en-rIN/strings.xml | 24 +++++++++++++++ .../values-mcc310-mnc004-en-rXC/strings.xml | 24 +++++++++++++++ .../values-mcc310-mnc004-es-rUS/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-es/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-et/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-eu/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-fa/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-fi/strings.xml | 24 +++++++++++++++ .../values-mcc310-mnc004-fr-rCA/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-fr/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-gl/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-gu/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-hi/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-hr/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-hu/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-hy/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-in/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-is/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-it/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-iw/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-ja/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-ka/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-kk/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-km/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-kn/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-ko/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-ky/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-lo/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-lt/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-lv/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-mk/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-ml/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-mn/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-mr/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-ms/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-my/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-nb/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-ne/strings.xml | 28 ++++++++++++++++++ .../res/values-mcc310-mnc004-nl/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-or/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-pa/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-pl/strings.xml | 24 +++++++++++++++ .../values-mcc310-mnc004-pt-rBR/strings.xml | 24 +++++++++++++++ .../values-mcc310-mnc004-pt-rPT/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-pt/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-ro/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-ru/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-si/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-sk/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-sl/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-sq/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-sr/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-sv/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-sw/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-ta/strings.xml | 28 ++++++++++++++++++ .../res/values-mcc310-mnc004-te/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-th/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-tl/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-tr/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-uk/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-ur/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-uz/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-vi/strings.xml | 24 +++++++++++++++ .../values-mcc310-mnc004-zh-rCN/strings.xml | 24 +++++++++++++++ .../values-mcc310-mnc004-zh-rHK/strings.xml | 24 +++++++++++++++ .../values-mcc310-mnc004-zh-rTW/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-zu/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-af/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-am/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-ar/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-as/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-az/strings.xml | 24 +++++++++++++++ .../strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-be/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-bg/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-bn/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-bs/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-ca/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-cs/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-da/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-de/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-el/strings.xml | 24 +++++++++++++++ .../values-mcc311-mnc480-en-rAU/strings.xml | 24 +++++++++++++++ .../values-mcc311-mnc480-en-rCA/strings.xml | 24 +++++++++++++++ .../values-mcc311-mnc480-en-rGB/strings.xml | 24 +++++++++++++++ .../values-mcc311-mnc480-en-rIN/strings.xml | 24 +++++++++++++++ .../values-mcc311-mnc480-en-rXC/strings.xml | 24 +++++++++++++++ .../values-mcc311-mnc480-es-rUS/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-es/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-et/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-eu/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-fa/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-fi/strings.xml | 24 +++++++++++++++ .../values-mcc311-mnc480-fr-rCA/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-fr/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-gl/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-gu/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-hi/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-hr/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-hu/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-hy/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-in/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-is/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-it/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-iw/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-ja/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-ka/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-kk/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-km/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-kn/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-ko/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-ky/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-lo/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-lt/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-lv/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-mk/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-ml/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-mn/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-mr/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-ms/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-my/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-nb/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-ne/strings.xml | 28 ++++++++++++++++++ .../res/values-mcc311-mnc480-nl/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-or/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-pa/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-pl/strings.xml | 24 +++++++++++++++ .../values-mcc311-mnc480-pt-rBR/strings.xml | 24 +++++++++++++++ .../values-mcc311-mnc480-pt-rPT/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-pt/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-ro/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-ru/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-si/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-sk/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-sl/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-sq/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-sr/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-sv/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-sw/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-ta/strings.xml | 28 ++++++++++++++++++ .../res/values-mcc311-mnc480-te/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-th/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-tl/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-tr/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-uk/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-ur/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-uz/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-vi/strings.xml | 24 +++++++++++++++ .../values-mcc311-mnc480-zh-rCN/strings.xml | 24 +++++++++++++++ .../values-mcc311-mnc480-zh-rHK/strings.xml | 24 +++++++++++++++ .../values-mcc311-mnc480-zh-rTW/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-zu/strings.xml | 24 +++++++++++++++ Tethering/res/values-mk/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-ml/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-mn/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-mr/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-ms/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-my/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-nb/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-ne/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-nl/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-or/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-pa/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-pl/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-pt-rBR/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-pt-rPT/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-pt/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-ro/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-ru/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-si/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-sk/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-sl/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-sq/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-sr/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-sv/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-sw/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-ta/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-te/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-th/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-tl/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-tr/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-uk/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-ur/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-uz/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-vi/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-zh-rCN/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-zh-rHK/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-zh-rTW/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-zu/strings.xml | 29 ++++++++++++++++--- 255 files changed, 6221 insertions(+), 340 deletions(-) create mode 100644 Tethering/res/values-mcc310-mnc004-af/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-am/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-ar/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-as/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-az/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-b+sr+Latn/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-be/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-bg/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-bn/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-bs/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-ca/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-cs/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-da/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-de/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-el/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-en-rAU/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-en-rCA/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-en-rGB/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-en-rIN/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-en-rXC/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-es-rUS/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-es/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-et/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-eu/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-fa/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-fi/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-fr-rCA/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-fr/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-gl/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-gu/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-hi/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-hr/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-hu/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-hy/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-in/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-is/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-it/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-iw/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-ja/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-ka/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-kk/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-km/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-kn/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-ko/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-ky/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-lo/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-lt/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-lv/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-mk/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-ml/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-mn/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-mr/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-ms/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-my/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-nb/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-ne/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-nl/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-or/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-pa/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-pl/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-pt-rBR/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-pt-rPT/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-pt/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-ro/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-ru/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-si/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-sk/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-sl/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-sq/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-sr/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-sv/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-sw/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-ta/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-te/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-th/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-tl/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-tr/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-uk/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-ur/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-uz/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-vi/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-zh-rCN/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-zh-rHK/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-zh-rTW/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-zu/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-af/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-am/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-ar/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-as/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-az/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-b+sr+Latn/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-be/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-bg/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-bn/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-bs/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-ca/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-cs/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-da/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-de/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-el/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-en-rAU/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-en-rCA/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-en-rGB/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-en-rIN/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-en-rXC/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-es-rUS/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-es/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-et/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-eu/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-fa/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-fi/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-fr-rCA/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-fr/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-gl/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-gu/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-hi/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-hr/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-hu/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-hy/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-in/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-is/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-it/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-iw/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-ja/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-ka/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-kk/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-km/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-kn/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-ko/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-ky/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-lo/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-lt/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-lv/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-mk/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-ml/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-mn/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-mr/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-ms/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-my/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-nb/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-ne/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-nl/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-or/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-pa/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-pl/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-pt-rBR/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-pt-rPT/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-pt/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-ro/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-ru/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-si/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-sk/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-sl/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-sq/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-sr/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-sv/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-sw/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-ta/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-te/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-th/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-tl/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-tr/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-uk/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-ur/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-uz/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-vi/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-zh-rCN/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-zh-rHK/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-zh-rTW/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-zu/strings.xml diff --git a/Tethering/res/values-af/strings.xml b/Tethering/res/values-af/strings.xml index 1258805378..056168b12e 100644 --- a/Tethering/res/values-af/strings.xml +++ b/Tethering/res/values-af/strings.xml @@ -1,8 +1,29 @@ + + - "Verbinding of Wi-Fi-warmkol aktief" - "Tik om op te stel." - "Verbinding is gedeaktiveer" - "Kontak jou administrateur vir besonderhede" + "Verbinding of warmkol is aktief" + "Tik om op te stel." + "Verbinding is gedeaktiveer" + "Kontak jou administrateur vir besonderhede" + "Warmkol- en verbindingstatus" + + + + + diff --git a/Tethering/res/values-am/strings.xml b/Tethering/res/values-am/strings.xml index 9c36192257..ac468dd144 100644 --- a/Tethering/res/values-am/strings.xml +++ b/Tethering/res/values-am/strings.xml @@ -1,8 +1,29 @@ + + - "መሰካት ወይም ገባሪ ድረስ ነጥብ" - "ለማዋቀር መታ ያድርጉ።" - "እንደ ሞደም መሰካት ተሰናክሏል" - "ለዝርዝሮች የእርስዎን አስተዳዳሪ ያነጋግሩ" + "እንደ ሞደም መሰካት ወይም መገናኛ ነጥብ ገባሪ" + "ለማዋቀር መታ ያድርጉ።" + "እንደ ሞደም መሰካት ተሰናክሏል" + "ለዝርዝሮች የእርስዎን አስተዳዳሪ ያነጋግሩ" + "መገናኛ ነጥብ እና እንደ ሞደም የመሰካት ሁኔታ" + + + + + diff --git a/Tethering/res/values-ar/strings.xml b/Tethering/res/values-ar/strings.xml index 9f84ce4090..7d5bad34da 100644 --- a/Tethering/res/values-ar/strings.xml +++ b/Tethering/res/values-ar/strings.xml @@ -1,8 +1,29 @@ + + - "النطاق أو نقطة الاتصال نشطة" - "انقر للإعداد." - "تم إيقاف التوصيل" - "اتصل بالمشرف للحصول على التفاصيل" + "النطاق نشط أو نقطة الاتصال نشطة" + "انقر للإعداد." + "التوصيل متوقف." + "تواصَل مع المشرف للحصول على التفاصيل." + "حالة نقطة الاتصال والتوصيل" + + + + + diff --git a/Tethering/res/values-as/strings.xml b/Tethering/res/values-as/strings.xml index 8855822e7c..091350455b 100644 --- a/Tethering/res/values-as/strings.xml +++ b/Tethering/res/values-as/strings.xml @@ -1,8 +1,29 @@ + + - "টেডাৰিং বা হটস্প\'ট সক্ৰিয় অৱস্থাত আছে" - "ছেট আপ কৰিবলৈ টিপক।" - "টেডাৰিং অক্ষম কৰি থোৱা হৈছে" - "সবিশেষ জানিবলৈ আপোনাৰ প্ৰশাসকৰ সৈতে যোগাযোগ কৰক" + "টে\'ডাৰিং অথবা হ\'টস্প\'ট সক্ৰিয় অৱস্থাত আছে" + "ছেট আপ কৰিবলৈ টিপক।" + "টে\'ডাৰিঙৰ সুবিধাটো অক্ষম কৰি থোৱা হৈছে" + "সবিশেষ জানিবলৈ আপোনাৰ প্ৰশাসকৰ সৈতে যোগাযোগ কৰক" + "হ’টস্প\'ট আৰু টে\'ডাৰিঙৰ স্থিতি" + + + + + diff --git a/Tethering/res/values-az/strings.xml b/Tethering/res/values-az/strings.xml index eba50eb636..dce70da178 100644 --- a/Tethering/res/values-az/strings.xml +++ b/Tethering/res/values-az/strings.xml @@ -1,8 +1,29 @@ + + - "Tezerinq və ya hotspot aktivdir" - "Quraşdırmaq üçün tıklayın." - "Birləşmə deaktivdir" - "Məlumat üçün adminlə əlaqə saxlayın" + "Birləşmə və ya hotspot aktivdir" + "Ayarlamaq üçün toxunun." + "Birləşmə deaktivdir" + "Detallar üçün adminlə əlaqə saxlayın" + "Hotspot & birləşmə statusu" + + + + + diff --git a/Tethering/res/values-b+sr+Latn/strings.xml b/Tethering/res/values-b+sr+Latn/strings.xml index 5b0e488ba5..b0774ec9a8 100644 --- a/Tethering/res/values-b+sr+Latn/strings.xml +++ b/Tethering/res/values-b+sr+Latn/strings.xml @@ -1,8 +1,29 @@ + + - "Aktivno povezivanje sa internetom preko mobilnog uređaja ili hotspot" - "Dodirnite da biste podesili." - "Privezivanje je onemogućeno" - "Potražite detalje od administratora" + "Privezivanje ili hotspot je aktivan" + "Dodirnite da biste podesili." + "Privezivanje je onemogućeno" + "Potražite detalje od administratora" + "Status hotspota i privezivanja" + + + + + diff --git a/Tethering/res/values-be/strings.xml b/Tethering/res/values-be/strings.xml index 5966c7155e..a8acebe2e9 100644 --- a/Tethering/res/values-be/strings.xml +++ b/Tethering/res/values-be/strings.xml @@ -1,8 +1,29 @@ + + - "USB-мадэм або хот-спот Wi-Fi актыўныя" - "Дакраніцеся, каб наладзіць." - "Рэжым мадэма адключаны" - "Звярніцеся да адміністратара па падрабязную інфармацыю" + "Мадэм або хот-спот актыўныя" + "Дакраніцеся, каб наладзіць." + "Рэжым мадэма выключаны" + "Звярніцеся да адміністратара па падрабязную інфармацыю" + "Стан \"Хот-спот і мадэм\"" + + + + + diff --git a/Tethering/res/values-bg/strings.xml b/Tethering/res/values-bg/strings.xml index ed58d7311a..94fb2d8f17 100644 --- a/Tethering/res/values-bg/strings.xml +++ b/Tethering/res/values-bg/strings.xml @@ -1,8 +1,29 @@ + + - "Има активна споделена връзка или безжична точка за достъп" - "Докоснете, за да настроите." - "Функцията за тетъринг е деактивирана" - "Свържете се с администратора си за подробности" + "Има активна споделена връзка или точка за достъп" + "Докоснете, за да настроите." + "Функцията за тетъринг е деактивирана" + "Свържете се с администратора си за подробности" + "Състояние на функцията за точка за достъп и тетъринг" + + + + + diff --git a/Tethering/res/values-bn/strings.xml b/Tethering/res/values-bn/strings.xml index 8d9880aa9a..aea02b9ddf 100644 --- a/Tethering/res/values-bn/strings.xml +++ b/Tethering/res/values-bn/strings.xml @@ -1,8 +1,29 @@ + + - "টিথারিং বা হটস্পট সক্রিয় আছে" - "সেট-আপ করার জন্য আলতো চাপুন৷" - "টিথারিং অক্ষম করা আছে" - "বিশদ বিবরণের জন্য প্রশাসকের সাথে যোগাযোগ করুন" + "টিথারিং বা হটস্পট চালু আছে" + "সেট-আপ করতে ট্যাপ করুন।" + "টিথারিং বন্ধ করা আছে" + "বিশদে জানতে অ্যাডমিনের সাথে যোগাযোগ করুন" + "হটস্পট ও টিথারিং স্ট্যাটাস" + + + + + diff --git a/Tethering/res/values-bs/strings.xml b/Tethering/res/values-bs/strings.xml index 2361b9dd38..de232724c5 100644 --- a/Tethering/res/values-bs/strings.xml +++ b/Tethering/res/values-bs/strings.xml @@ -1,8 +1,29 @@ + + - "Uređaj dijeli vezu ili djeluje kao pristupna tačka" - "Dodirnite za postavke" - "Povezivanje putem mobitela je onemogućeno" - "Kontaktirajte svog administratora za dodatne detalje" + "Aktivno je povezivanje putem mobitela ili pristupna tačka" + "Dodirnite da postavite." + "Povezivanje putem mobitela je onemogućeno" + "Kontaktirajte svog administratora za detalje" + "Status pristupne tačke i povezivanja putem mobitela" + + + + + diff --git a/Tethering/res/values-ca/strings.xml b/Tethering/res/values-ca/strings.xml index 6752b519e2..88b795c1f8 100644 --- a/Tethering/res/values-ca/strings.xml +++ b/Tethering/res/values-ca/strings.xml @@ -1,8 +1,29 @@ + + - "Compartició de xarxa o punt d\'accés Wi-Fi activat" - "Toca per configurar." - "La compartició de xarxa està desactivada" - "Contacta amb el teu administrador per obtenir més informació" + "Compartició de xarxa o punt d\'accés Wi‑Fi actius" + "Toca per configurar." + "La compartició de xarxa està desactivada" + "Contacta amb el teu administrador per obtenir més informació" + "Estat del punt d\'accés Wi‑Fi i de la compartició de xarxa" + + + + + diff --git a/Tethering/res/values-cs/strings.xml b/Tethering/res/values-cs/strings.xml index 5fdd53adf1..8c1b83bf3e 100644 --- a/Tethering/res/values-cs/strings.xml +++ b/Tethering/res/values-cs/strings.xml @@ -1,8 +1,29 @@ + + - "Sdílené připojení nebo hotspot je aktivní." - "Klepnutím zahájíte nastavení." - "Tethering je zakázán" - "O podrobnosti požádejte administrátora" + "Tethering nebo hotspot je aktivní" + "Klepnutím zahájíte nastavení." + "Tethering je zakázán" + "O podrobnosti požádejte administrátora" + "Stav hotspotu a tetheringu" + + + + + diff --git a/Tethering/res/values-da/strings.xml b/Tethering/res/values-da/strings.xml index 2775dfa551..f413e70548 100644 --- a/Tethering/res/values-da/strings.xml +++ b/Tethering/res/values-da/strings.xml @@ -1,8 +1,29 @@ + + - "Netdeling eller hotspot er aktivt" - "Tryk for at konfigurere" - "Netdeling er deaktiveret" - "Kontakt din administrator for at få oplysninger" + "Netdeling eller hotspot er aktivt" + "Tryk for at konfigurere." + "Netdeling er deaktiveret" + "Kontakt din administrator for at få oplysninger" + "Status for hotspot og netdeling" + + + + + diff --git a/Tethering/res/values-de/strings.xml b/Tethering/res/values-de/strings.xml index 9046cd5e11..f057d7824e 100644 --- a/Tethering/res/values-de/strings.xml +++ b/Tethering/res/values-de/strings.xml @@ -1,8 +1,29 @@ + + - "Tethering oder Hotspot aktiv" - "Zum Einrichten tippen." - "Tethering ist deaktiviert" - "Bitte wende dich für weitere Informationen an den Administrator" + "Tethering oder Hotspot aktiv" + "Zum Einrichten tippen." + "Tethering ist deaktiviert" + "Bitte wende dich für weitere Informationen an den Administrator" + "Hotspot- und Tethering-Status" + + + + + diff --git a/Tethering/res/values-el/strings.xml b/Tethering/res/values-el/strings.xml index 3b9f53733b..b3c986bdaf 100644 --- a/Tethering/res/values-el/strings.xml +++ b/Tethering/res/values-el/strings.xml @@ -1,8 +1,29 @@ + + - "Πρόσδεση ή σύνδεση σημείου πρόσβασης ενεργή" - "Πατήστε για ρύθμιση." - "Η σύνδεση είναι απενεργοποιημένη" - "Επικοινωνήστε με τον διαχειριστή σας για λεπτομέρειες" + "Πρόσδεση ή σύνδεση σημείου πρόσβασης ενεργή" + "Πατήστε για ρύθμιση." + "Η σύνδεση είναι απενεργοποιημένη" + "Επικοινωνήστε με τον διαχειριστή σας για λεπτομέρειες" + "Κατάσταση σημείου πρόσβασης Wi-Fi και σύνδεσης" + + + + + diff --git a/Tethering/res/values-en-rAU/strings.xml b/Tethering/res/values-en-rAU/strings.xml index 56b88a5fb3..769e01208a 100644 --- a/Tethering/res/values-en-rAU/strings.xml +++ b/Tethering/res/values-en-rAU/strings.xml @@ -1,8 +1,29 @@ + + - "Tethering or hotspot active" - "Tap to set up." - "Tethering is disabled" - "Contact your admin for details" + "Tethering or hotspot active" + "Tap to set up." + "Tethering is disabled" + "Contact your admin for details" + "Hotspot and tethering status" + + + + + diff --git a/Tethering/res/values-en-rCA/strings.xml b/Tethering/res/values-en-rCA/strings.xml index 56b88a5fb3..769e01208a 100644 --- a/Tethering/res/values-en-rCA/strings.xml +++ b/Tethering/res/values-en-rCA/strings.xml @@ -1,8 +1,29 @@ + + - "Tethering or hotspot active" - "Tap to set up." - "Tethering is disabled" - "Contact your admin for details" + "Tethering or hotspot active" + "Tap to set up." + "Tethering is disabled" + "Contact your admin for details" + "Hotspot and tethering status" + + + + + diff --git a/Tethering/res/values-en-rGB/strings.xml b/Tethering/res/values-en-rGB/strings.xml index 56b88a5fb3..769e01208a 100644 --- a/Tethering/res/values-en-rGB/strings.xml +++ b/Tethering/res/values-en-rGB/strings.xml @@ -1,8 +1,29 @@ + + - "Tethering or hotspot active" - "Tap to set up." - "Tethering is disabled" - "Contact your admin for details" + "Tethering or hotspot active" + "Tap to set up." + "Tethering is disabled" + "Contact your admin for details" + "Hotspot and tethering status" + + + + + diff --git a/Tethering/res/values-en-rIN/strings.xml b/Tethering/res/values-en-rIN/strings.xml index 56b88a5fb3..769e01208a 100644 --- a/Tethering/res/values-en-rIN/strings.xml +++ b/Tethering/res/values-en-rIN/strings.xml @@ -1,8 +1,29 @@ + + - "Tethering or hotspot active" - "Tap to set up." - "Tethering is disabled" - "Contact your admin for details" + "Tethering or hotspot active" + "Tap to set up." + "Tethering is disabled" + "Contact your admin for details" + "Hotspot and tethering status" + + + + + diff --git a/Tethering/res/values-en-rXC/strings.xml b/Tethering/res/values-en-rXC/strings.xml index 7f47fc89d2..f1674bed4e 100644 --- a/Tethering/res/values-en-rXC/strings.xml +++ b/Tethering/res/values-en-rXC/strings.xml @@ -1,8 +1,29 @@ + + - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‏‎‏‎‏‎‏‏‎‏‎‎‏‏‎‏‏‎‏‏‏‎‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‎‏‏‎‎‎‏‏‎‏‎‏‏‏‎‏‎‎‏‎Tethering or hotspot active‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‎‏‎‏‎‏‎‏‎‎‎‏‏‏‎‏‏‏‏‏‎‎‎‎‏‏‏‏‎‎‎‎‏‏‏‏‎‎‎‏‏‏‎‏‏‏‏‎‎‏‎‏‎‎‎‏‎Tap to set up.‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‏‏‎‏‎‏‎‎‏‏‎‎‏‎‏‎‎‎‏‏‏‏‎‏‏‏‏‏‏‎‏‏‏‏‎‏‎‎‎‏‏‏‎‎‎‏‎‎‏‎‏‎‏‏‎Tethering is disabled‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‎‏‏‎‏‏‏‎‎‏‎‏‏‎‏‏‎‏‎‏‎‏‎‎‏‏‎‎‎‎‎‎‎‏‎‎‏‎‏‏‏‎‏‎‎‎‏‏‎‎‎‎‏‏‏‏‎Contact your admin for details‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‎‏‎‏‏‏‏‏‎‏‏‏‏‎‏‏‎‎‎‏‎‎‎‎‎‏‏‎‏‏‏‏‎‎‎‎‏‏‏‎‎‏‎‎‏‎‏‏‎‏‏‎‎‎‎‎Tethering or hotspot active‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‎‎‏‏‏‎‎‏‏‎‎‎‏‏‏‎‎‎‎‎‎‎‏‏‏‎‎‏‏‏‏‎‏‏‏‏‏‎‏‏‏‏‎‎‏‎‏‏‎‏‎‎‎‏‏‎‎‎‎Tap to set up.‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‏‏‎‏‏‎‎‏‎‎‎‏‎‏‎‎‏‎‎‏‎‎‎‏‎‎‎‎‏‏‏‎‏‎‎‎‏‏‏‎‎‎‏‎‏‎‎‎‏‏‎‎‏‏‏‏‏‎Tethering is disabled‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‏‏‏‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‏‎‏‏‏‏‏‎‎‏‏‎‎‏‎‎‏‎‏‎‎‏‏‏‎‏‎‏‎‏‎‎‏‎‎‎Contact your admin for details‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‏‏‎‏‏‎‏‎‎‎‎‏‏‎‎‏‎‎‏‏‎‎‎‎‏‏‎‏‏‎‏‏‎‎‎‏‏‏‏‏‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎Hotspot & tethering status‎‏‎‎‏‎" + + + + + diff --git a/Tethering/res/values-es-rUS/strings.xml b/Tethering/res/values-es-rUS/strings.xml index e4618b8cec..63689f4399 100644 --- a/Tethering/res/values-es-rUS/strings.xml +++ b/Tethering/res/values-es-rUS/strings.xml @@ -1,8 +1,29 @@ + + - "Anclaje a red o zona activa conectados" - "Presiona para configurar." - "Se inhabilitó la conexión mediante dispositivo portátil" - "Para obtener más información, comunícate con el administrador" + "Conexión a red o hotspot conectados" + "Presiona para configurar esta opción." + "Se inhabilitó la conexión mediante dispositivo portátil" + "Para obtener más información, comunícate con el administrador" + "Estado del hotspot y la conexión mediante dispositivo portátil" + + + + + diff --git a/Tethering/res/values-es/strings.xml b/Tethering/res/values-es/strings.xml index 8dc1575ce8..9a34ed5e38 100644 --- a/Tethering/res/values-es/strings.xml +++ b/Tethering/res/values-es/strings.xml @@ -1,8 +1,29 @@ + + - "Compartir conexión/Zona Wi-Fi activada" - "Toca para configurar." - "La conexión compartida está inhabilitada" - "Ponte en contacto con el administrador para obtener más información" + "Conexión compartida o punto de acceso activos" + "Toca para configurar." + "La conexión compartida está inhabilitada" + "Solicita más información a tu administrador" + "Estado del punto de acceso y de la conexión compartida" + + + + + diff --git a/Tethering/res/values-et/strings.xml b/Tethering/res/values-et/strings.xml index 872c8a74cc..0970341ab0 100644 --- a/Tethering/res/values-et/strings.xml +++ b/Tethering/res/values-et/strings.xml @@ -1,8 +1,29 @@ + + - "Jagamine või kuumkoht on aktiivne" - "Puudutage seadistamiseks." - "Jagamine on keelatud" - "Lisateabe saamiseks võtke ühendust oma administraatoriga" + "Jagamine või kuumkoht on aktiivne" + "Puudutage seadistamiseks." + "Jagamine on keelatud" + "Lisateabe saamiseks võtke ühendust oma administraatoriga" + "Kuumkoha ja jagamise olek" + + + + + diff --git a/Tethering/res/values-eu/strings.xml b/Tethering/res/values-eu/strings.xml index 6c4605e616..632019e2ef 100644 --- a/Tethering/res/values-eu/strings.xml +++ b/Tethering/res/values-eu/strings.xml @@ -1,8 +1,29 @@ + + - "Konexioa partekatzea edo sare publikoa aktibo" - "Sakatu konfiguratzeko." - "Desgaituta dago konexioa partekatzeko aukera" - "Xehetasunak lortzeko, jarri administratzailearekin harremanetan" + "Konexioa partekatzea edo wifi-gunea aktibo dago" + "Sakatu konfiguratzeko." + "Desgaituta dago konexioa partekatzeko aukera" + "Xehetasunak lortzeko, jarri administratzailearekin harremanetan" + "Wifi-gunearen eta konexioa partekatzeko eginbidearen egoera" + + + + + diff --git a/Tethering/res/values-fa/strings.xml b/Tethering/res/values-fa/strings.xml index bc2ee23609..2e21c85fa1 100644 --- a/Tethering/res/values-fa/strings.xml +++ b/Tethering/res/values-fa/strings.xml @@ -1,8 +1,29 @@ + + - "اشتراک‌گذاری اینترنت یا نقطه اتصال فعال" - "برای راه‌اندازی ضربه بزنید." - "اشتراک‌گذاری اینترنت غیرفعال است" - "برای جزئیات، با سرپرستتان تماس بگیرید" + "اشتراک‌گذاری اینترنت یا نقطه اتصال فعال" + "برای راه‌اندازی ضربه بزنید." + "اشتراک‌گذاری اینترنت غیرفعال است" + "برای جزئیات، با سرپرستتان تماس بگیرید" + "وضعیت نقطه اتصال و اشتراک‌گذاری اینترنت" + + + + + diff --git a/Tethering/res/values-fi/strings.xml b/Tethering/res/values-fi/strings.xml index ff0fca6502..413db3f0f8 100644 --- a/Tethering/res/values-fi/strings.xml +++ b/Tethering/res/values-fi/strings.xml @@ -1,8 +1,29 @@ + + - "Internetin jakaminen tai yhteyspiste käytössä" - "Määritä napauttamalla." - "Yhteyden jakaminen poistettu käytöstä" - "Kysy lisätietoja järjestelmänvalvojalta." + "Yhteyden jakaminen tai hotspot käytössä" + "Ota käyttöön napauttamalla." + "Yhteyden jakaminen on poistettu käytöstä" + "Pyydä lisätietoja järjestelmänvalvojalta" + "Hotspotin ja yhteyden jakamisen tila" + + + + + diff --git a/Tethering/res/values-fr-rCA/strings.xml b/Tethering/res/values-fr-rCA/strings.xml index 1f5df0ee0c..eb2e4ba540 100644 --- a/Tethering/res/values-fr-rCA/strings.xml +++ b/Tethering/res/values-fr-rCA/strings.xml @@ -1,8 +1,29 @@ + + - "Partage de connexion ou point d\'accès sans fil activé" - "Touchez pour configurer." - "Le partage de connexion est désactivé" - "Communiquez avec votre administrateur pour obtenir plus de détails" + "Partage de connexion ou point d\'accès sans fil activé" + "Touchez pour configurer." + "Le partage de connexion est désactivé" + "Communiquez avec votre administrateur pour obtenir plus de détails" + "Point d\'accès et partage de connexion" + + + + + diff --git a/Tethering/res/values-fr/strings.xml b/Tethering/res/values-fr/strings.xml index daf7c9d830..22259c52ab 100644 --- a/Tethering/res/values-fr/strings.xml +++ b/Tethering/res/values-fr/strings.xml @@ -1,8 +1,29 @@ + + - "Partage de connexion ou point d\'accès sans fil activé" - "Appuyez ici pour configurer." - "Le partage de connexion est désactivé" - "Pour en savoir plus, contactez votre administrateur" + "Partage de connexion ou point d\'accès activé" + "Appuyez pour effectuer la configuration." + "Le partage de connexion est désactivé" + "Pour en savoir plus, contactez votre administrateur" + "État du point d\'accès et du partage de connexion" + + + + + diff --git a/Tethering/res/values-gl/strings.xml b/Tethering/res/values-gl/strings.xml index 0d16a1de09..ded82fcd54 100644 --- a/Tethering/res/values-gl/strings.xml +++ b/Tethering/res/values-gl/strings.xml @@ -1,8 +1,29 @@ + + - "Conexión compartida ou zona wifi activada" - "Tocar para configurar." - "A conexión compartida está desactivada" - "Contacta co administrador para obter información" + "Conexión compartida ou zona wifi activada" + "Toca para configurar." + "A conexión compartida está desactivada" + "Contacta co administrador para obter información" + "Estado da zona wifi e da conexión compartida" + + + + + diff --git a/Tethering/res/values-gu/strings.xml b/Tethering/res/values-gu/strings.xml index 9d6b02f85f..7cbbc2de3d 100644 --- a/Tethering/res/values-gu/strings.xml +++ b/Tethering/res/values-gu/strings.xml @@ -1,8 +1,29 @@ + + - "ટિથરિંગ અથવા હૉટસ્પૉટ સક્રિય" - "સેટ કરવા માટે ટૅપ કરો." - "ટિથરિંગ અક્ષમ કરેલ છે" - "વિગતો માટે તમારા વ્યવસ્થાપકનો સંપર્ક કરો" + "ઇન્ટરનેટ શેર કરવાની સુવિધા અથવા હૉટસ્પૉટ સક્રિય છે" + "સેટઅપ કરવા માટે ટૅપ કરો." + "ઇન્ટરનેટ શેર કરવાની સુવિધા બંધ કરી છે" + "વિગતો માટે તમારા વ્યવસ્થાપકનો સંપર્ક કરો" + "હૉટસ્પૉટ અને ઇન્ટરનેટ શેર કરવાની સુવિધાનું સ્ટેટસ" + + + + + diff --git a/Tethering/res/values-hi/strings.xml b/Tethering/res/values-hi/strings.xml index 9c29d9a8f9..08af81b826 100644 --- a/Tethering/res/values-hi/strings.xml +++ b/Tethering/res/values-hi/strings.xml @@ -1,8 +1,29 @@ + + - "टेदरिंग या हॉटस्‍पॉट सक्रिय" - "सेट करने के लिए टैप करें." - "टेदरिंग अक्षम है" - "जानकारी के लिए अपने एडमिन से संपर्क करें" + "टेदरिंग या हॉटस्पॉट चालू है" + "सेट अप करने के लिए टैप करें." + "टेदरिंग बंद है" + "जानकारी के लिए अपने एडमिन से संपर्क करें" + "हॉटस्पॉट और टेदरिंग की स्थिति" + + + + + diff --git a/Tethering/res/values-hr/strings.xml b/Tethering/res/values-hr/strings.xml index d0d25bb755..827c135f20 100644 --- a/Tethering/res/values-hr/strings.xml +++ b/Tethering/res/values-hr/strings.xml @@ -1,8 +1,29 @@ + + - "Ograničenje ili aktivan hotspot" - "Dodirnite da biste postavili." - "Modemsko je povezivanje onemogućeno" - "Obratite se administratoru da biste saznali pojedinosti" + "Modemsko povezivanje ili žarišna točka aktivni" + "Dodirnite da biste postavili." + "Modemsko je povezivanje onemogućeno" + "Obratite se administratoru da biste saznali pojedinosti" + "Status žarišne točke i modemskog povezivanja" + + + + + diff --git a/Tethering/res/values-hu/strings.xml b/Tethering/res/values-hu/strings.xml index 3129659923..eb68d6babf 100644 --- a/Tethering/res/values-hu/strings.xml +++ b/Tethering/res/values-hu/strings.xml @@ -1,8 +1,29 @@ + + - "Megosztás vagy aktív hotspot" - "Koppintson a beállításhoz." - "Az internetmegosztás le van tiltva" - "A részletekért forduljon rendszergazdájához" + "Megosztás vagy aktív hotspot" + "Koppintson a beállításhoz." + "Az internetmegosztás le van tiltva" + "A részletekért forduljon rendszergazdájához" + "Hotspot és internetmegosztás állapota" + + + + + diff --git a/Tethering/res/values-hy/strings.xml b/Tethering/res/values-hy/strings.xml index 8ba6435fd5..912941e538 100644 --- a/Tethering/res/values-hy/strings.xml +++ b/Tethering/res/values-hy/strings.xml @@ -1,8 +1,29 @@ + + - "Մոդեմի ռեժիմը միացված է" - "Հպեք՝ կարգավորելու համար:" - "Մոդեմի ռեժիմն անջատված է" - "Մանրամասների համար դիմեք ձեր ադմինիստրատորին" + "Մոդեմի ռեժիմը միացված է" + "Հպեք՝ կարգավորելու համար։" + "Մոդեմի ռեժիմն անջատված է" + "Մանրամասների համար դիմեք ձեր ադմինիստրատորին" + "Թեժ կետի և մոդեմի ռեժիմի կարգավիճակը" + + + + + diff --git a/Tethering/res/values-in/strings.xml b/Tethering/res/values-in/strings.xml index 1e093ab237..a4e175a439 100644 --- a/Tethering/res/values-in/strings.xml +++ b/Tethering/res/values-in/strings.xml @@ -1,8 +1,29 @@ + + - "Tethering (Penambatan) atau hotspot aktif" - "Ketuk untuk menyiapkan." - "Tethering dinonaktifkan" - "Hubungi admin untuk mengetahui detailnya" + "Tethering atau hotspot aktif" + "Ketuk untuk menyiapkan." + "Tethering dinonaktifkan" + "Hubungi admin untuk mengetahui detailnya" + "Status hotspot & tethering" + + + + + diff --git a/Tethering/res/values-is/strings.xml b/Tethering/res/values-is/strings.xml index f5769d5344..e9f6670bcd 100644 --- a/Tethering/res/values-is/strings.xml +++ b/Tethering/res/values-is/strings.xml @@ -1,8 +1,29 @@ + + - "Kveikt á tjóðrun eða aðgangsstað" - "Ýttu til að setja upp." - "Slökkt er á tjóðrun" - "Hafðu samband við kerfisstjórann til að fá upplýsingar" + "Kveikt á tjóðrun eða aðgangsstað" + "Ýttu til að setja upp." + "Slökkt er á tjóðrun" + "Hafðu samband við kerfisstjórann til að fá upplýsingar" + "Staða heits reits og tjóðrunar" + + + + + diff --git a/Tethering/res/values-it/strings.xml b/Tethering/res/values-it/strings.xml index e0b3724325..ffb9196f5e 100644 --- a/Tethering/res/values-it/strings.xml +++ b/Tethering/res/values-it/strings.xml @@ -1,8 +1,29 @@ + + - "Tethering oppure hotspot attivo" - "Tocca per impostare." - "Tethering disattivato" - "Contatta il tuo amministratore per avere informazioni dettagliate" + "Hotspot o tethering attivo" + "Tocca per impostare." + "Tethering disattivato" + "Contatta il tuo amministratore per avere informazioni dettagliate" + "Stato hotspot e tethering" + + + + + diff --git a/Tethering/res/values-iw/strings.xml b/Tethering/res/values-iw/strings.xml index c002c44b23..7adcb47350 100644 --- a/Tethering/res/values-iw/strings.xml +++ b/Tethering/res/values-iw/strings.xml @@ -1,8 +1,29 @@ + + - "שיתוף אינטרנט פעיל" - "הקש כדי להגדיר." - "שיתוף האינטרנט בין ניידים מושבת" - "לפרטים, יש לפנות למנהל המערכת" + "נקודה לשיתוף אינטרנט או שיתוף אינטרנט בין מכשירים: בסטטוס פעיל" + "יש להקיש כדי להגדיר." + "שיתוף האינטרנט בין מכשירים מושבת" + "לפרטים, יש לפנות למנהל המערכת" + "סטטוס של נקודה לשיתוף אינטרנט ושיתוף אינטרנט בין מכשירים" + + + + + diff --git a/Tethering/res/values-ja/strings.xml b/Tethering/res/values-ja/strings.xml index 314bde00df..f68a73010b 100644 --- a/Tethering/res/values-ja/strings.xml +++ b/Tethering/res/values-ja/strings.xml @@ -1,8 +1,29 @@ + + - "テザリングまたはアクセスポイントが有効です" - "タップしてセットアップします。" - "テザリングは無効に設定されています" - "詳しくは、管理者にお問い合わせください" + "テザリングまたはアクセス ポイントが有効です" + "タップしてセットアップします。" + "テザリングは無効に設定されています" + "詳しくは、管理者にお問い合わせください" + "アクセス ポイントとテザリングのステータス" + + + + + diff --git a/Tethering/res/values-ka/strings.xml b/Tethering/res/values-ka/strings.xml index 7bbd81d343..7c22e82bd3 100644 --- a/Tethering/res/values-ka/strings.xml +++ b/Tethering/res/values-ka/strings.xml @@ -1,8 +1,29 @@ + + - "ტეტერინგი ან უსადენო ქსელი აქტიურია" - "შეეხეთ დასაყენებლად." - "ტეტერინგი გათიშულია" - "დამატებითი ინფორმაციისთვის დაუკავშირდით თქვენს ადმინისტრატორს" + "ტეტერინგი ან უსადენო ქსელი აქტიურია" + "შეეხეთ დასაყენებლად." + "ტეტერინგი გათიშულია" + "დამატებითი ინფორმაციისთვის დაუკავშირდით თქვენს ადმინისტრატორს" + "უსადენო ქსელის და ტეტერინგის სტატუსი" + + + + + diff --git a/Tethering/res/values-kk/strings.xml b/Tethering/res/values-kk/strings.xml index 7fd87a1596..0857d06de2 100644 --- a/Tethering/res/values-kk/strings.xml +++ b/Tethering/res/values-kk/strings.xml @@ -1,8 +1,29 @@ + + - "Тетеринг немесе хотспот қосулы" - "Реттеу үшін түртіңіз." - "Тетеринг өшірілді" - "Мәліметтерді әкімшіден алыңыз" + "Тетеринг немесе хотспот қосулы" + "Реттеу үшін түртіңіз." + "Тетеринг өшірілді." + "Мәліметтерді әкімшіден алыңыз." + "Хотспот және тетеринг күйі" + + + + + diff --git a/Tethering/res/values-km/strings.xml b/Tethering/res/values-km/strings.xml index 2f85224679..536e3d1703 100644 --- a/Tethering/res/values-km/strings.xml +++ b/Tethering/res/values-km/strings.xml @@ -1,8 +1,29 @@ + + - "ភ្ជាប់ ឬ​ហតស្ពត​សកម្ម" - "ប៉ះដើម្បីកំណត់" - "ការភ្ជាប់​ត្រូវបានបិទ" - "ទាក់ទងអ្នកគ្រប់គ្រង​របស់អ្នកសម្រាប់​ព័ត៌មានលម្អិត" + "ការភ្ជាប់ ឬហតស្ប៉ត​កំពុងដំណើរការ" + "ចុច​ដើម្បី​រៀបចំ។" + "ការភ្ជាប់​ត្រូវបានបិទ" + "ទាក់ទងអ្នកគ្រប់គ្រង​របស់អ្នក ដើម្បីទទួលបានព័ត៌មានលម្អិត" + "ស្ថានភាពនៃការភ្ជាប់ និងហតស្ប៉ត" + + + + + diff --git a/Tethering/res/values-kn/strings.xml b/Tethering/res/values-kn/strings.xml index f11a83ea40..32f54926f4 100644 --- a/Tethering/res/values-kn/strings.xml +++ b/Tethering/res/values-kn/strings.xml @@ -1,8 +1,29 @@ + + - "ಟೆಥರಿಂಗ್ ಅಥವಾ ಹಾಟ್‌ಸ್ಪಾಟ್ ಸಕ್ರಿಯವಾಗಿದೆ" - "ಹೊಂದಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ." - "ಟೆಥರಿಂಗ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ" - "ವಿವರಗಳಿಗಾಗಿ ನಿಮ್ಮ ನಿರ್ವಾಹಕರನ್ನು ಸಂಪರ್ಕಿಸಿ" + "ಟೆಥರಿಂಗ್ ಅಥವಾ ಹಾಟ್‌ಸ್ಪಾಟ್ ಸಕ್ರಿಯವಾಗಿದೆ" + "ಸೆಟಪ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ." + "ಟೆಥರಿಂಗ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ" + "ವಿವರಗಳಿಗಾಗಿ ನಿಮ್ಮ ನಿರ್ವಾಹಕರನ್ನು ಸಂಪರ್ಕಿಸಿ" + "ಹಾಟ್‌ಸ್ಪಾಟ್ ಮತ್ತು ಟೆಥರಿಂಗ್‌ ಸ್ಥಿತಿ" + + + + + diff --git a/Tethering/res/values-ko/strings.xml b/Tethering/res/values-ko/strings.xml index 57f24f5b1a..156b24786d 100644 --- a/Tethering/res/values-ko/strings.xml +++ b/Tethering/res/values-ko/strings.xml @@ -1,8 +1,29 @@ + + - "테더링 또는 핫스팟 사용" - "설정하려면 탭하세요." - "테더링이 사용 중지됨" - "자세한 정보는 관리자에게 문의하세요." + "테더링 또는 핫스팟 사용" + "설정하려면 탭하세요." + "테더링이 사용 중지됨" + "자세한 정보는 관리자에게 문의하세요." + "핫스팟 및 테더링 상태" + + + + + diff --git a/Tethering/res/values-ky/strings.xml b/Tethering/res/values-ky/strings.xml index 79854859d4..18ee5fd357 100644 --- a/Tethering/res/values-ky/strings.xml +++ b/Tethering/res/values-ky/strings.xml @@ -1,8 +1,29 @@ + + - "Жалгаштыруу же хотспот жандырылган" - "Жөндөө үчүн таптап коюңуз." - "Жалгаштыруу функциясы өчүрүлгөн" - "Кеңири маалымат үчүн администраторуңузга кайрылыңыз" + "Модем режими күйүп турат" + "Жөндөө үчүн таптап коюңуз." + "Телефонду модем катары колдонууга болбойт" + "Кеңири маалымат үчүн администраторуңузга кайрылыңыз" + "Байланыш түйүнүнүн жана модем режиминин статусу" + + + + + diff --git a/Tethering/res/values-lo/strings.xml b/Tethering/res/values-lo/strings.xml index 78f1585f60..b12767018c 100644 --- a/Tethering/res/values-lo/strings.xml +++ b/Tethering/res/values-lo/strings.xml @@ -1,8 +1,29 @@ + + - "ເປີດ​ການ​ປ່ອຍ​ສັນຍານ ຫຼື​ຮັອດສະປອດ​ແລ້ວ" - "ແຕະເພື່ອຕັ້ງຄ່າ." - "ການປ່ອຍສັນຍານຖືກປິດໄວ້" - "ຕິດຕໍ່ຜູ້ເບິ່ງແຍງລະບົບສຳລັບລາຍລະອຽດ" + "ເປີດການປ່ອຍສັນຍານ ຫຼື ຮັອດສະປອດແລ້ວ" + "ແຕະເພື່ອຕັ້ງຄ່າ." + "ການປ່ອຍສັນຍານຖືກປິດໄວ້" + "ຕິດຕໍ່ຜູ້ເບິ່ງແຍງລະບົບສຳລັບລາຍລະອຽດ" + "ສະຖານະຮັອດສະປອດ ແລະ ການປ່ອຍສັນຍານ" + + + + + diff --git a/Tethering/res/values-lt/strings.xml b/Tethering/res/values-lt/strings.xml index ebff8ac9d1..8427baf39f 100644 --- a/Tethering/res/values-lt/strings.xml +++ b/Tethering/res/values-lt/strings.xml @@ -1,8 +1,29 @@ + + - "Susietas ar aktyvus" - "Palieskite, kad nustatytumėte." - "Įrenginio kaip modemo naudojimas išjungtas" - "Jei reikia išsamios informacijos, susisiekite su administratoriumi" + "Įrenginys naudojamas kaip modemas arba įjungtas viešosios interneto prieigos taškas" + "Palieskite, kad nustatytumėte." + "Įrenginio kaip modemo naudojimas išjungtas" + "Jei reikia išsamios informacijos, susisiekite su administratoriumi" + "Viešosios interneto prieigos taško ir įrenginio kaip modemo naudojimo būsena" + + + + + diff --git a/Tethering/res/values-lv/strings.xml b/Tethering/res/values-lv/strings.xml index 54d0048b52..aa2d6990e0 100644 --- a/Tethering/res/values-lv/strings.xml +++ b/Tethering/res/values-lv/strings.xml @@ -1,8 +1,29 @@ + + - "Piesaiste vai tīklājs ir aktīvs." - "Pieskarieties, lai iestatītu." - "Piesaiste ir atspējota" - "Lai iegūtu detalizētu informāciju, sazinieties ar savu administratoru." + "Piesaiste vai tīklājs ir aktīvs." + "Pieskarieties, lai to iestatītu." + "Piesaiste ir atspējota" + "Lai iegūtu detalizētu informāciju, sazinieties ar savu administratoru." + "Tīklāja un piesaistes statuss" + + + + + diff --git a/Tethering/res/values-mcc310-mnc004-af/strings.xml b/Tethering/res/values-mcc310-mnc004-af/strings.xml new file mode 100644 index 0000000000..19d659c6ce --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-af/strings.xml @@ -0,0 +1,24 @@ + + + + + "Verbinding het nie internet nie" + "Toestelle kan nie koppel nie" + "Skakel verbinding af" + "Warmkol of verbinding is aan" + "Bykomende heffings kan geld terwyl jy swerf" + diff --git a/Tethering/res/values-mcc310-mnc004-am/strings.xml b/Tethering/res/values-mcc310-mnc004-am/strings.xml new file mode 100644 index 0000000000..8995430b4f --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-am/strings.xml @@ -0,0 +1,24 @@ + + + + + "ማስተሳሰር ምንም በይነመረብ የለውም" + "መሣሪያዎችን ማገናኘት አይቻልም" + "ማስተሳሰርን አጥፋ" + "መገናኛ ነጥብ ወይም ማስተሳሰር በርቷል" + "በሚያንዣብብበት ጊዜ ተጨማሪ ክፍያዎች ተፈጻሚ ሊሆኑ ይችላሉ" + diff --git a/Tethering/res/values-mcc310-mnc004-ar/strings.xml b/Tethering/res/values-mcc310-mnc004-ar/strings.xml new file mode 100644 index 0000000000..54f3b5389a --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-ar/strings.xml @@ -0,0 +1,24 @@ + + + + + "ما مِن اتصال بالإنترنت خلال التوصيل" + "تعذّر اتصال الأجهزة" + "إيقاف التوصيل" + "نقطة الاتصال أو التوصيل مفعّلان" + "قد يتم تطبيق رسوم إضافية أثناء التجوال." + diff --git a/Tethering/res/values-mcc310-mnc004-as/strings.xml b/Tethering/res/values-mcc310-mnc004-as/strings.xml new file mode 100644 index 0000000000..e215141c9e --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-as/strings.xml @@ -0,0 +1,24 @@ + + + + + "টে\'ডাৰিঙৰ ইণ্টাৰনেট নাই" + "ডিভাইচসমূহ সংযোগ কৰিব নোৱাৰি" + "টে\'ডাৰিং অফ কৰক" + "হটস্পট অথবা টে\'ডাৰিং অন আছে" + "ৰ\'মিঙত থাকিলে অতিৰিক্ত মাচুল প্ৰযোজ্য হ’ব পাৰে" + diff --git a/Tethering/res/values-mcc310-mnc004-az/strings.xml b/Tethering/res/values-mcc310-mnc004-az/strings.xml new file mode 100644 index 0000000000..1fd8e4c963 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-az/strings.xml @@ -0,0 +1,24 @@ + + + + + "Modemin internetə girişi yoxdur" + "Cihazları qoşmaq mümkün deyil" + "Modemi deaktiv edin" + "Hotspot və ya modem aktivdir" + "Rouminq zamanı əlavə ödənişlər tətbiq edilə bilər" + diff --git a/Tethering/res/values-mcc310-mnc004-b+sr+Latn/strings.xml b/Tethering/res/values-mcc310-mnc004-b+sr+Latn/strings.xml new file mode 100644 index 0000000000..1abe4f3aa3 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-b+sr+Latn/strings.xml @@ -0,0 +1,24 @@ + + + + + "Privezivanje nema pristup internetu" + "Povezivanje uređaja nije uspelo" + "Isključi privezivanje" + "Uključen je hotspot ili privezivanje" + "Možda važe dodatni troškovi u romingu" + diff --git a/Tethering/res/values-mcc310-mnc004-be/strings.xml b/Tethering/res/values-mcc310-mnc004-be/strings.xml new file mode 100644 index 0000000000..38dbd1e391 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-be/strings.xml @@ -0,0 +1,24 @@ + + + + + "Рэжым мадэма выкарыстоўваецца без доступу да інтэрнэту" + "Не ўдалося падключыць прылады" + "Выключыць рэжым мадэма" + "Хот-спот або рэжым мадэма ўключаны" + "Пры выкарыстанні роўмінгу можа спаганяцца дадатковая плата" + diff --git a/Tethering/res/values-mcc310-mnc004-bg/strings.xml b/Tethering/res/values-mcc310-mnc004-bg/strings.xml new file mode 100644 index 0000000000..04b44db5c1 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-bg/strings.xml @@ -0,0 +1,24 @@ + + + + + "Тетърингът няма връзка с интернет" + "Устройствата не могат да установят връзка" + "Изключване на тетъринга" + "Точката за достъп или тетърингът са включени" + "Възможно е да ви бъдат начислени допълнителни такси при роуминг" + diff --git a/Tethering/res/values-mcc310-mnc004-bn/strings.xml b/Tethering/res/values-mcc310-mnc004-bn/strings.xml new file mode 100644 index 0000000000..579d1be1c1 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-bn/strings.xml @@ -0,0 +1,24 @@ + + + + + "টিথারিং করার জন্য কোনও ইন্টারনেট কানেকশন নেই" + "ডিভাইস কানেক্ট করতে পারছে না" + "টিথারিং বন্ধ করুন" + "হটস্পট বা টিথারিং চালু আছে" + "রোমিংয়ের সময় অতিরিক্ত চার্জ করা হতে পারে" + diff --git a/Tethering/res/values-mcc310-mnc004-bs/strings.xml b/Tethering/res/values-mcc310-mnc004-bs/strings.xml new file mode 100644 index 0000000000..9ce3efe6c3 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-bs/strings.xml @@ -0,0 +1,24 @@ + + + + + "Povezivanje putem mobitela nema internet" + "Uređaji se ne mogu povezati" + "Isključi povezivanje putem mobitela" + "Pristupna tačka ili povezivanje putem mobitela je uključeno" + "Mogu nastati dodatni troškovi u romingu" + diff --git a/Tethering/res/values-mcc310-mnc004-ca/strings.xml b/Tethering/res/values-mcc310-mnc004-ca/strings.xml new file mode 100644 index 0000000000..46d4c35b9b --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-ca/strings.xml @@ -0,0 +1,24 @@ + + + + + "La compartició de xarxa no té accés a Internet" + "No es poden connectar els dispositius" + "Desactiva la compartició de xarxa" + "S\'ha activat el punt d\'accés Wi‑Fi o la compartició de xarxa" + "És possible que s\'apliquin costos addicionals en itinerància" + diff --git a/Tethering/res/values-mcc310-mnc004-cs/strings.xml b/Tethering/res/values-mcc310-mnc004-cs/strings.xml new file mode 100644 index 0000000000..cc13860b3d --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-cs/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tethering nemá připojení k internetu" + "Zařízení se nemůžou připojit" + "Vypnout tethering" + "Je zapnutý hotspot nebo tethering" + "Při roamingu mohou být účtovány dodatečné poplatky" + diff --git a/Tethering/res/values-mcc310-mnc004-da/strings.xml b/Tethering/res/values-mcc310-mnc004-da/strings.xml new file mode 100644 index 0000000000..92c3ae1156 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-da/strings.xml @@ -0,0 +1,24 @@ + + + + + "Netdeling har ingen internetforbindelse" + "Enheder kan ikke oprette forbindelse" + "Deaktiver netdeling" + "Hotspot eller netdeling er aktiveret" + "Der opkræves muligvis yderligere gebyrer ved roaming" + diff --git a/Tethering/res/values-mcc310-mnc004-de/strings.xml b/Tethering/res/values-mcc310-mnc004-de/strings.xml new file mode 100644 index 0000000000..967eb4db2e --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-de/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tethering hat keinen Internetzugriff" + "Geräte können sich nicht verbinden" + "Tethering deaktivieren" + "Hotspot oder Tethering ist aktiviert" + "Für das Roaming können zusätzliche Gebühren anfallen" + diff --git a/Tethering/res/values-mcc310-mnc004-el/strings.xml b/Tethering/res/values-mcc310-mnc004-el/strings.xml new file mode 100644 index 0000000000..5fb497451f --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-el/strings.xml @@ -0,0 +1,24 @@ + + + + + "Η σύνδεση δεν έχει πρόσβαση στο διαδίκτυο" + "Δεν είναι δυνατή η σύνδεση των συσκευών" + "Απενεργοποιήστε τη σύνδεση" + "Ενεργό σημείο πρόσβασης Wi-Fi ή ενεργή σύνδεση" + "Ενδέχεται να ισχύουν επιπλέον χρεώσεις κατά την περιαγωγή." + diff --git a/Tethering/res/values-mcc310-mnc004-en-rAU/strings.xml b/Tethering/res/values-mcc310-mnc004-en-rAU/strings.xml new file mode 100644 index 0000000000..45647f93f2 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-en-rAU/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tethering has no Internet" + "Devices can’t connect" + "Turn off tethering" + "Hotspot or tethering is on" + "Additional charges may apply while roaming" + diff --git a/Tethering/res/values-mcc310-mnc004-en-rCA/strings.xml b/Tethering/res/values-mcc310-mnc004-en-rCA/strings.xml new file mode 100644 index 0000000000..45647f93f2 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-en-rCA/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tethering has no Internet" + "Devices can’t connect" + "Turn off tethering" + "Hotspot or tethering is on" + "Additional charges may apply while roaming" + diff --git a/Tethering/res/values-mcc310-mnc004-en-rGB/strings.xml b/Tethering/res/values-mcc310-mnc004-en-rGB/strings.xml new file mode 100644 index 0000000000..45647f93f2 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-en-rGB/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tethering has no Internet" + "Devices can’t connect" + "Turn off tethering" + "Hotspot or tethering is on" + "Additional charges may apply while roaming" + diff --git a/Tethering/res/values-mcc310-mnc004-en-rIN/strings.xml b/Tethering/res/values-mcc310-mnc004-en-rIN/strings.xml new file mode 100644 index 0000000000..45647f93f2 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-en-rIN/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tethering has no Internet" + "Devices can’t connect" + "Turn off tethering" + "Hotspot or tethering is on" + "Additional charges may apply while roaming" + diff --git a/Tethering/res/values-mcc310-mnc004-en-rXC/strings.xml b/Tethering/res/values-mcc310-mnc004-en-rXC/strings.xml new file mode 100644 index 0000000000..7877074afc --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-en-rXC/strings.xml @@ -0,0 +1,24 @@ + + + + + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‏‎‎‏‏‏‎‎‏‎‎‏‏‎‏‎‎‎‏‎‏‎‏‏‏‏‏‎‎‏‏‎‎‎‎‏‎‏‏‏‏‎‏‎‏‎‎‎‏‏‏‎‏‎‎‎Tethering has no internet‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‎‏‎‏‎‏‏‏‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‎‎‎‎‏‏‏‎‎‏‏‎‏‏‎‎‏‎‏‎‎‎‎‏‏‏‎Devices can’t connect‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‎‎‏‏‏‎‏‏‏‎‏‎‎‏‎‏‎‏‎‎‏‏‏‎‎‎‏‏‎‎‏‏‎‏‎‏‏‏‏‎‎‎‏‎‏‏‎‎‎‏‎‏‎‎‎‎Turn off tethering‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‏‏‏‎‏‏‎‏‎‏‎‏‎‏‏‎‎‎‎‏‎‎‎‏‏‎‎‎‎‎‎‎‎‏‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‎‎‏‏‏‎Hotspot or tethering is on‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‎‏‎‎‎‏‏‏‏‏‏‏‎‏‏‎‏‏‏‏‎‎‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‎‎‏‏‏‏‏‎‏‎‏‎‏‏‏‏‎‎Additional charges may apply while roaming‎‏‎‎‏‎" + diff --git a/Tethering/res/values-mcc310-mnc004-es-rUS/strings.xml b/Tethering/res/values-mcc310-mnc004-es-rUS/strings.xml new file mode 100644 index 0000000000..08edd81a6b --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-es-rUS/strings.xml @@ -0,0 +1,24 @@ + + + + + "La conexión mediante dispositivo móvil no tiene Internet" + "No se pueden conectar los dispositivos" + "Desactivar conexión mediante dispositivo móvil" + "Se activó el hotspot o la conexión mediante dispositivo móvil" + "Es posible que se apliquen cargos adicionales por roaming" + diff --git a/Tethering/res/values-mcc310-mnc004-es/strings.xml b/Tethering/res/values-mcc310-mnc004-es/strings.xml new file mode 100644 index 0000000000..79f51d00e2 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-es/strings.xml @@ -0,0 +1,24 @@ + + + + + "La conexión no se puede compartir, porque no hay acceso a Internet" + "Los dispositivos no se pueden conectar" + "Desactivar conexión compartida" + "Punto de acceso o conexión compartida activados" + "Puede que se apliquen cargos adicionales en itinerancia" + diff --git a/Tethering/res/values-mcc310-mnc004-et/strings.xml b/Tethering/res/values-mcc310-mnc004-et/strings.xml new file mode 100644 index 0000000000..2da5f8a6d6 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-et/strings.xml @@ -0,0 +1,24 @@ + + + + + "Jagamisel puudub internetiühendus" + "Seadmed ei saa ühendust luua" + "Lülita jagamine välja" + "Kuumkoht või jagamine on sisse lülitatud" + "Rändluse kasutamisega võivad kaasneda lisatasud" + diff --git a/Tethering/res/values-mcc310-mnc004-eu/strings.xml b/Tethering/res/values-mcc310-mnc004-eu/strings.xml new file mode 100644 index 0000000000..2073f2806c --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-eu/strings.xml @@ -0,0 +1,24 @@ + + + + + "Konexioa partekatzeko aukerak ez du Interneteko konexiorik" + "Ezin dira konektatu gailuak" + "Desaktibatu konexioa partekatzeko aukera" + "Wifi-gunea edo konexioa partekatzeko aukera aktibatuta dago" + "Baliteke kostu gehigarriak ordaindu behar izatea ibiltaritzan" + diff --git a/Tethering/res/values-mcc310-mnc004-fa/strings.xml b/Tethering/res/values-mcc310-mnc004-fa/strings.xml new file mode 100644 index 0000000000..e21b2a0852 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-fa/strings.xml @@ -0,0 +1,24 @@ + + + + + "«اشتراک‌گذاری اینترنت» به اینترنت دسترسی ندارد" + "دستگاه‌ها متصل نمی‌شوند" + "خاموش کردن «اشتراک‌گذاری اینترنت»" + "«نقطه اتصال» یا «اشتراک‌گذاری اینترنت» روشن است" + "ممکن است درحین فراگردی تغییرات دیگر اعمال شود" + diff --git a/Tethering/res/values-mcc310-mnc004-fi/strings.xml b/Tethering/res/values-mcc310-mnc004-fi/strings.xml new file mode 100644 index 0000000000..88b0b13eb4 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-fi/strings.xml @@ -0,0 +1,24 @@ + + + + + "Ei jaettavaa internetyhteyttä" + "Laitteet eivät voi muodostaa yhteyttä" + "Laita yhteyden jakaminen pois päältä" + "Hotspot tai yhteyden jakaminen on päällä" + "Roaming voi aiheuttaa lisämaksuja" + diff --git a/Tethering/res/values-mcc310-mnc004-fr-rCA/strings.xml b/Tethering/res/values-mcc310-mnc004-fr-rCA/strings.xml new file mode 100644 index 0000000000..3b781bc8db --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-fr-rCA/strings.xml @@ -0,0 +1,24 @@ + + + + + "Le partage de connexion n\'est pas connecté à Internet" + "Impossible de connecter les appareils" + "Désactiver le partage de connexion" + "Le point d\'accès ou le partage de connexion est activé" + "En itinérance, des frais supplémentaires peuvent s\'appliquer" + diff --git a/Tethering/res/values-mcc310-mnc004-fr/strings.xml b/Tethering/res/values-mcc310-mnc004-fr/strings.xml new file mode 100644 index 0000000000..51d7203c36 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-fr/strings.xml @@ -0,0 +1,24 @@ + + + + + "Aucune connexion à Internet n\'est disponible pour le partage de connexion" + "Impossible de connecter les appareils" + "Désactiver le partage de connexion" + "Le point d\'accès ou le partage de connexion est activé" + "En itinérance, des frais supplémentaires peuvent s\'appliquer" + diff --git a/Tethering/res/values-mcc310-mnc004-gl/strings.xml b/Tethering/res/values-mcc310-mnc004-gl/strings.xml new file mode 100644 index 0000000000..008ccb475d --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-gl/strings.xml @@ -0,0 +1,24 @@ + + + + + "A conexión compartida non ten Internet" + "Non se puideron conectar os dispositivos" + "Desactivar conexión compartida" + "Está activada a zona wifi ou a conexión compartida" + "Pódense aplicar cargos adicionais en itinerancia" + diff --git a/Tethering/res/values-mcc310-mnc004-gu/strings.xml b/Tethering/res/values-mcc310-mnc004-gu/strings.xml new file mode 100644 index 0000000000..f2e3b4df78 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-gu/strings.xml @@ -0,0 +1,24 @@ + + + + + "ઇન્ટરનેટ શેર કરવાની સુવિધામાં ઇન્ટરનેટ નથી" + "ડિવાઇસ કનેક્ટ કરી શકાતા નથી" + "ઇન્ટરનેટ શેર કરવાની સુવિધા બંધ કરો" + "હૉટસ્પૉટ અથવા ઇન્ટરનેટ શેર કરવાની સુવિધા ચાલુ છે" + "રોમિંગમાં વધારાના શુલ્ક લાગી શકે છે" + diff --git a/Tethering/res/values-mcc310-mnc004-hi/strings.xml b/Tethering/res/values-mcc310-mnc004-hi/strings.xml new file mode 100644 index 0000000000..b11839d760 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-hi/strings.xml @@ -0,0 +1,24 @@ + + + + + "टेदरिंग से इंटरनेट नहीं चल रहा" + "डिवाइस कनेक्ट नहीं हो पा रहे" + "टेदरिंग बंद करें" + "हॉटस्पॉट या टेदरिंग चालू है" + "रोमिंग के दौरान अतिरिक्त शुल्क लग सकता है" + diff --git a/Tethering/res/values-mcc310-mnc004-hr/strings.xml b/Tethering/res/values-mcc310-mnc004-hr/strings.xml new file mode 100644 index 0000000000..0a5aca25b1 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-hr/strings.xml @@ -0,0 +1,24 @@ + + + + + "Modemsko povezivanje nema internet" + "Uređaji se ne mogu povezati" + "Isključivanje modemskog povezivanja" + "Uključena je žarišna točka ili modemsko povezivanje" + "U roamingu su mogući dodatni troškovi" + diff --git a/Tethering/res/values-mcc310-mnc004-hu/strings.xml b/Tethering/res/values-mcc310-mnc004-hu/strings.xml new file mode 100644 index 0000000000..21c689a44e --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-hu/strings.xml @@ -0,0 +1,24 @@ + + + + + "Nincs internetkapcsolat az internet megosztásához" + "Az eszközök nem tudnak csatlakozni" + "Internetmegosztás kikapcsolása" + "A hotspot vagy az internetmegosztás be van kapcsolva" + "Roaming során további díjak léphetnek fel" + diff --git a/Tethering/res/values-mcc310-mnc004-hy/strings.xml b/Tethering/res/values-mcc310-mnc004-hy/strings.xml new file mode 100644 index 0000000000..689d92870e --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-hy/strings.xml @@ -0,0 +1,24 @@ + + + + + "Մոդեմի ռեժիմի կապը բացակայում է" + "Չհաջողվեց միացնել սարքը" + "Անջատել մոդեմի ռեժիմը" + "Թեժ կետը կամ մոդեմի ռեժիմը միացված է" + "Ռոումինգում կարող են լրացուցիչ վճարներ գանձվել" + diff --git a/Tethering/res/values-mcc310-mnc004-in/strings.xml b/Tethering/res/values-mcc310-mnc004-in/strings.xml new file mode 100644 index 0000000000..a5f4d19abf --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-in/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tidak ada koneksi internet di tethering" + "Perangkat tidak dapat terhubung" + "Nonaktifkan tethering" + "Hotspot atau tethering aktif" + "Biaya tambahan mungkin berlaku saat roaming" + diff --git a/Tethering/res/values-mcc310-mnc004-is/strings.xml b/Tethering/res/values-mcc310-mnc004-is/strings.xml new file mode 100644 index 0000000000..fc7e8aaf4e --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-is/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tjóðrun er ekki með internettengingu" + "Tæki geta ekki tengst" + "Slökkva á tjóðrun" + "Kveikt er á heitum reit eða tjóðrun" + "Viðbótargjöld kunna að eiga við í reiki" + diff --git a/Tethering/res/values-mcc310-mnc004-it/strings.xml b/Tethering/res/values-mcc310-mnc004-it/strings.xml new file mode 100644 index 0000000000..6456dd1b80 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-it/strings.xml @@ -0,0 +1,24 @@ + + + + + "Nessuna connessione a Internet per il tethering" + "Impossibile connettere i dispositivi" + "Disattiva il tethering" + "Hotspot o tethering attivi" + "Potrebbero essere applicati costi aggiuntivi durante il roaming" + diff --git a/Tethering/res/values-mcc310-mnc004-iw/strings.xml b/Tethering/res/values-mcc310-mnc004-iw/strings.xml new file mode 100644 index 0000000000..46b24bd3c5 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-iw/strings.xml @@ -0,0 +1,24 @@ + + + + + "אי אפשר להפעיל את תכונת שיתוף האינטרנט בין מכשירים כי אין חיבור לאינטרנט" + "למכשירים אין אפשרות להתחבר" + "השבתה של שיתוף האינטרנט בין מכשירים" + "תכונת הנקודה לשיתוף אינטרנט או תכונת שיתוף האינטרנט בין מכשירים פועלת" + "ייתכנו חיובים נוספים בעת נדידה" + diff --git a/Tethering/res/values-mcc310-mnc004-ja/strings.xml b/Tethering/res/values-mcc310-mnc004-ja/strings.xml new file mode 100644 index 0000000000..e6eb277b90 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-ja/strings.xml @@ -0,0 +1,24 @@ + + + + + "テザリングがインターネットに接続されていません" + "デバイスを接続できません" + "テザリングを OFF にする" + "アクセス ポイントまたはテザリングが ON です" + "ローミング時に追加料金が発生することがあります" + diff --git a/Tethering/res/values-mcc310-mnc004-ka/strings.xml b/Tethering/res/values-mcc310-mnc004-ka/strings.xml new file mode 100644 index 0000000000..aeddd7101d --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-ka/strings.xml @@ -0,0 +1,24 @@ + + + + + "ტეტერინგს არ აქვს ინტერნეტზე წვდომა" + "მოწყობილობები ვერ ახერხებენ დაკავშირებას" + "ტეტერინგის გამორთვა" + "ჩართულია უსადენო ქსელი ან ტეტერინგი" + "როუმინგის გამოყენებისას შეიძლება ჩამოგეჭრათ დამატებითი საფასური" + diff --git a/Tethering/res/values-mcc310-mnc004-kk/strings.xml b/Tethering/res/values-mcc310-mnc004-kk/strings.xml new file mode 100644 index 0000000000..255f0a276f --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-kk/strings.xml @@ -0,0 +1,24 @@ + + + + + "Тетеринг режимі интернет байланысынсыз пайдаланылуда" + "Құрылғыларды байланыстыру мүмкін емес" + "Тетерингіні өшіру" + "Хотспот немесе тетеринг қосулы" + "Роуминг кезінде қосымша ақы алынуы мүмкін." + diff --git a/Tethering/res/values-mcc310-mnc004-km/strings.xml b/Tethering/res/values-mcc310-mnc004-km/strings.xml new file mode 100644 index 0000000000..2bceb1cf77 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-km/strings.xml @@ -0,0 +1,24 @@ + + + + + "ការភ្ជាប់​មិនមានអ៊ីនធឺណិត​ទេ" + "មិនអាច​ភ្ជាប់ឧបករណ៍​បានទេ" + "បិទការភ្ជាប់" + "ហតស្ប៉ត ឬការភ្ជាប់​ត្រូវបានបើក" + "អាចមាន​ការគិតថ្លៃ​បន្ថែម នៅពេល​រ៉ូមីង" + diff --git a/Tethering/res/values-mcc310-mnc004-kn/strings.xml b/Tethering/res/values-mcc310-mnc004-kn/strings.xml new file mode 100644 index 0000000000..ed769305a6 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-kn/strings.xml @@ -0,0 +1,24 @@ + + + + + "ಟೆಥರಿಂಗ್‌ ಯಾವುದೇ ಇಂಟರ್ನೆಟ್ ಕನೆಕ್ಷನ್ ಹೊಂದಿಲ್ಲ" + "ಸಾಧನಗಳನ್ನು ಕನೆಕ್ಟ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ" + "ಟೆಥರಿಂಗ್‌ ಆಫ್ ಮಾಡಿ" + "ಹಾಟ್‌ಸ್ಪಾಟ್ ಅಥವಾ ಟೆಥರಿಂಗ್‌ ಆನ್ ಆಗಿದೆ" + "ರೋಮಿಂಗ್‌ನಲ್ಲಿರುವಾಗ ಹೆಚ್ಚುವರಿ ಶುಲ್ಕಗಳು ಅನ್ವಯವಾಗಬಹುದು" + diff --git a/Tethering/res/values-mcc310-mnc004-ko/strings.xml b/Tethering/res/values-mcc310-mnc004-ko/strings.xml new file mode 100644 index 0000000000..6e504941eb --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-ko/strings.xml @@ -0,0 +1,24 @@ + + + + + "테더링으로 인터넷을 사용할 수 없음" + "기기에서 연결할 수 없음" + "테더링 사용 중지" + "핫스팟 또는 테더링 켜짐" + "로밍 중에는 추가 요금이 발생할 수 있습니다." + diff --git a/Tethering/res/values-mcc310-mnc004-ky/strings.xml b/Tethering/res/values-mcc310-mnc004-ky/strings.xml new file mode 100644 index 0000000000..d68128b9a5 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-ky/strings.xml @@ -0,0 +1,24 @@ + + + + + "Модем режими Интернети жок колдонулууда" + "Түзмөктөр туташпай жатат" + "Модем режимин өчүрүү" + "Байланыш түйүнү же модем режими күйүк" + "Роумингде кошумча акы алынышы мүмкүн" + diff --git a/Tethering/res/values-mcc310-mnc004-lo/strings.xml b/Tethering/res/values-mcc310-mnc004-lo/strings.xml new file mode 100644 index 0000000000..03e134a0fc --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-lo/strings.xml @@ -0,0 +1,24 @@ + + + + + "ການປ່ອຍສັນຍານບໍ່ມີອິນເຕີເນັດ" + "ອຸປະກອນບໍ່ສາມາດເຊື່ອມຕໍ່ໄດ້" + "ປິດການປ່ອຍສັນຍານ" + "ເປີດໃຊ້ຮັອດສະປອດ ຫຼື ການປ່ອຍສັນຍານຢູ່" + "ອາດມີຄ່າໃຊ້ຈ່າຍເພີ່ມເຕີມໃນລະຫວ່າງການໂຣມມິງ" + diff --git a/Tethering/res/values-mcc310-mnc004-lt/strings.xml b/Tethering/res/values-mcc310-mnc004-lt/strings.xml new file mode 100644 index 0000000000..652cedc6e6 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-lt/strings.xml @@ -0,0 +1,24 @@ + + + + + "Nėra įrenginio kaip modemo naudojimo interneto ryšio" + "Nepavyko susieti įrenginių" + "Išjungti įrenginio kaip modemo naudojimą" + "Įjungtas viešosios interneto prieigos taškas arba įrenginio kaip modemo naudojimas" + "Veikiant tarptinkliniam ryšiui gali būti taikomi papildomi mokesčiai" + diff --git a/Tethering/res/values-mcc310-mnc004-lv/strings.xml b/Tethering/res/values-mcc310-mnc004-lv/strings.xml new file mode 100644 index 0000000000..221972298c --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-lv/strings.xml @@ -0,0 +1,24 @@ + + + + + "Piesaistei nav interneta savienojuma" + "Nevar savienot ierīces" + "Izslēgt piesaisti" + "Ir ieslēgts tīklājs vai piesaiste" + "Viesabonēšanas laikā var tikt piemērota papildu samaksa" + diff --git a/Tethering/res/values-mcc310-mnc004-mk/strings.xml b/Tethering/res/values-mcc310-mnc004-mk/strings.xml new file mode 100644 index 0000000000..227f9e3466 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-mk/strings.xml @@ -0,0 +1,24 @@ + + + + + "Нема интернет преку мобилен" + "Уредите не може да се поврзат" + "Исклучи интернет преку мобилен" + "Точката на пристап или интернетот преку мобилен е вклучен" + "При роаминг може да се наплатат дополнителни трошоци" + diff --git a/Tethering/res/values-mcc310-mnc004-ml/strings.xml b/Tethering/res/values-mcc310-mnc004-ml/strings.xml new file mode 100644 index 0000000000..ec43885126 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-ml/strings.xml @@ -0,0 +1,24 @@ + + + + + "ടെതറിംഗിന് ഇന്റർനെറ്റ് ഇല്ല" + "ഉപകരണങ്ങൾ കണക്റ്റ് ചെയ്യാനാവില്ല" + "ടെതറിംഗ് ഓഫാക്കുക" + "ഹോട്ട്‌സ്‌പോട്ട് അല്ലെങ്കിൽ ടെതറിംഗ് ഓണാണ്" + "റോമിംഗ് ചെയ്യുമ്പോൾ അധിക നിരക്കുകൾ ബാധകമായേക്കാം" + diff --git a/Tethering/res/values-mcc310-mnc004-mn/strings.xml b/Tethering/res/values-mcc310-mnc004-mn/strings.xml new file mode 100644 index 0000000000..e263573799 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-mn/strings.xml @@ -0,0 +1,24 @@ + + + + + "Модемд интернэт алга байна" + "Төхөөрөмжүүд холбогдох боломжгүй байна" + "Модем болгохыг унтраах" + "Сүлжээний цэг эсвэл модем болгох асаалттай байна" + "Роумингийн үеэр нэмэлт төлбөр нэхэмжилж болзошгүй" + diff --git a/Tethering/res/values-mcc310-mnc004-mr/strings.xml b/Tethering/res/values-mcc310-mnc004-mr/strings.xml new file mode 100644 index 0000000000..adf845d078 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-mr/strings.xml @@ -0,0 +1,24 @@ + + + + + "टेदरिंगला इंटरनेट नाही" + "डिव्हाइस कनेक्ट होऊ शकत नाहीत" + "टेदरिंग बंद करा" + "हॉटस्पॉट किंवा टेदरिंग सुरू आहे" + "रोमिंगदरम्यान अतिरिक्त शुल्क लागू होऊ शकतात" + diff --git a/Tethering/res/values-mcc310-mnc004-ms/strings.xml b/Tethering/res/values-mcc310-mnc004-ms/strings.xml new file mode 100644 index 0000000000..f65c451e4c --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-ms/strings.xml @@ -0,0 +1,24 @@ + + + + + "Penambatan tiada Internet" + "Peranti tidak dapat disambungkan" + "Matikan penambatan" + "Tempat liputan atau penambatan dihidupkan" + "Caj tambahan mungkin digunakan semasa perayauan" + diff --git a/Tethering/res/values-mcc310-mnc004-my/strings.xml b/Tethering/res/values-mcc310-mnc004-my/strings.xml new file mode 100644 index 0000000000..4118e775cd --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-my/strings.xml @@ -0,0 +1,24 @@ + + + + + "မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်းတွင် အင်တာနက် မရှိပါ" + "စက်များ ချိတ်ဆက်၍ မရပါ" + "မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်း ပိတ်ရန်" + "ဟော့စပေါ့ (သို့) မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်း ဖွင့်ထားသည်" + "ပြင်ပကွန်ရက်နှင့် ချိတ်ဆက်သည့်အခါ နောက်ထပ်ကျသင့်မှုများ ရှိနိုင်သည်" + diff --git a/Tethering/res/values-mcc310-mnc004-nb/strings.xml b/Tethering/res/values-mcc310-mnc004-nb/strings.xml new file mode 100644 index 0000000000..36853583ce --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-nb/strings.xml @@ -0,0 +1,24 @@ + + + + + "Internettdeling har ikke internettilgang" + "Enhetene kan ikke koble til" + "Slå av internettdeling" + "Wi-Fi-sone eller internettdeling er på" + "Ytterligere kostnader kan påløpe under roaming" + diff --git a/Tethering/res/values-mcc310-mnc004-ne/strings.xml b/Tethering/res/values-mcc310-mnc004-ne/strings.xml new file mode 100644 index 0000000000..2a7330098f --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-ne/strings.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + "रोमिङ सेवा प्रयोग गर्दा अतिरिक्त शुल्क लाग्न सक्छ" + diff --git a/Tethering/res/values-mcc310-mnc004-nl/strings.xml b/Tethering/res/values-mcc310-mnc004-nl/strings.xml new file mode 100644 index 0000000000..1d888942f4 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-nl/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tethering heeft geen internet" + "Apparaten kunnen niet worden verbonden" + "Tethering uitschakelen" + "Hotspot of tethering is ingeschakeld" + "Er kunnen extra kosten voor roaming in rekening worden gebracht." + diff --git a/Tethering/res/values-mcc310-mnc004-or/strings.xml b/Tethering/res/values-mcc310-mnc004-or/strings.xml new file mode 100644 index 0000000000..8038815fe8 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-or/strings.xml @@ -0,0 +1,24 @@ + + + + + "ଟିଥରିଂ ପାଇଁ କୌଣସି ଇଣ୍ଟର୍ନେଟ୍ ସଂଯୋଗ ନାହିଁ" + "ଡିଭାଇସଗୁଡ଼ିକ ସଂଯୋଗ କରାଯାଇପାରିବ ନାହିଁ" + "ଟିଥରିଂ ବନ୍ଦ କରନ୍ତୁ" + "ହଟସ୍ପଟ୍ କିମ୍ବା ଟିଥରିଂ ଚାଲୁ ଅଛି" + "ରୋମିଂରେ ଥିବା ସମୟରେ ଅତିରିକ୍ତ ଶୁଳ୍କ ଲାଗୁ ହୋଇପାରେ" + diff --git a/Tethering/res/values-mcc310-mnc004-pa/strings.xml b/Tethering/res/values-mcc310-mnc004-pa/strings.xml new file mode 100644 index 0000000000..819833eab0 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-pa/strings.xml @@ -0,0 +1,24 @@ + + + + + "ਟੈਦਰਿੰਗ ਕੋਲ ਇੰਟਰਨੈੱਟ ਪਹੁੰਚ ਨਹੀਂ ਹੈ" + "ਡੀਵਾਈਸ ਕਨੈਕਟ ਨਹੀਂ ਕੀਤੇ ਜਾ ਸਕਦੇ" + "ਟੈਦਰਿੰਗ ਬੰਦ ਕਰੋ" + "ਹੌਟਸਪੌਟ ਜਾਂ ਟੈਦਰਿੰਗ ਚਾਲੂ ਹੈ" + "ਰੋਮਿੰਗ ਦੌਰਾਨ ਵਧੀਕ ਖਰਚੇ ਲਾਗੂ ਹੋ ਸਕਦੇ ਹਨ" + diff --git a/Tethering/res/values-mcc310-mnc004-pl/strings.xml b/Tethering/res/values-mcc310-mnc004-pl/strings.xml new file mode 100644 index 0000000000..65e4380e39 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-pl/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tethering nie ma internetu" + "Urządzenia nie mogą się połączyć" + "Wyłącz tethering" + "Hotspot lub tethering jest włączony" + "Podczas korzystania z roamingu mogą zostać naliczone dodatkowe opłaty" + diff --git a/Tethering/res/values-mcc310-mnc004-pt-rBR/strings.xml b/Tethering/res/values-mcc310-mnc004-pt-rBR/strings.xml new file mode 100644 index 0000000000..d8866170c1 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-pt-rBR/strings.xml @@ -0,0 +1,24 @@ + + + + + "O tethering não tem Internet" + "Não é possível conectar os dispositivos" + "Desativar o tethering" + "Ponto de acesso ou tethering ativado" + "Pode haver cobranças extras durante o roaming" + diff --git a/Tethering/res/values-mcc310-mnc004-pt-rPT/strings.xml b/Tethering/res/values-mcc310-mnc004-pt-rPT/strings.xml new file mode 100644 index 0000000000..bfd45ca0a3 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-pt-rPT/strings.xml @@ -0,0 +1,24 @@ + + + + + "A ligação (à Internet) via telemóvel não tem Internet" + "Não é possível ligar os dispositivos" + "Desativar ligação (à Internet) via telemóvel" + "A zona Wi-Fi ou a ligação (à Internet) via telemóvel está ativada" + "Podem aplicar-se custos adicionais em roaming." + diff --git a/Tethering/res/values-mcc310-mnc004-pt/strings.xml b/Tethering/res/values-mcc310-mnc004-pt/strings.xml new file mode 100644 index 0000000000..d8866170c1 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-pt/strings.xml @@ -0,0 +1,24 @@ + + + + + "O tethering não tem Internet" + "Não é possível conectar os dispositivos" + "Desativar o tethering" + "Ponto de acesso ou tethering ativado" + "Pode haver cobranças extras durante o roaming" + diff --git a/Tethering/res/values-mcc310-mnc004-ro/strings.xml b/Tethering/res/values-mcc310-mnc004-ro/strings.xml new file mode 100644 index 0000000000..8d87a9e516 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-ro/strings.xml @@ -0,0 +1,24 @@ + + + + + "Procesul de tethering nu are internet" + "Dispozitivele nu se pot conecta" + "Dezactivați procesul de tethering" + "S-a activat hotspotul sau tethering" + "Se pot aplica taxe suplimentare pentru roaming" + diff --git a/Tethering/res/values-mcc310-mnc004-ru/strings.xml b/Tethering/res/values-mcc310-mnc004-ru/strings.xml new file mode 100644 index 0000000000..dbdb9ebe49 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-ru/strings.xml @@ -0,0 +1,24 @@ + + + + + "Режим модема используется без доступа к Интернету" + "Невозможно подключить устройства." + "Отключить режим модема" + "Включены точка доступа или режим модема" + "За использование услуг связи в роуминге может взиматься дополнительная плата." + diff --git a/Tethering/res/values-mcc310-mnc004-si/strings.xml b/Tethering/res/values-mcc310-mnc004-si/strings.xml new file mode 100644 index 0000000000..d8301e41c2 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-si/strings.xml @@ -0,0 +1,24 @@ + + + + + "ටෙදරින් හට අන්තර්ජාලය නැත" + "උපාංගවලට සම්බන්ධ විය නොහැකිය" + "ටෙදරින් ක්‍රියාවිරහිත කරන්න" + "හොට්ස්පොට් හෝ ටෙදරින් ක්‍රියාත්මකයි" + "රෝමිං අතරතුර අමතර ගාස්තු අදාළ විය හැකිය" + diff --git a/Tethering/res/values-mcc310-mnc004-sk/strings.xml b/Tethering/res/values-mcc310-mnc004-sk/strings.xml new file mode 100644 index 0000000000..bef71363f4 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-sk/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tethering nemá internetové pripojenie" + "Zariadenia sa nemôžu pripojiť" + "Vypnúť tethering" + "Je zapnutý hotspot alebo tethering" + "Počas roamingu vám môžu byť účtované ďalšie poplatky" + diff --git a/Tethering/res/values-mcc310-mnc004-sl/strings.xml b/Tethering/res/values-mcc310-mnc004-sl/strings.xml new file mode 100644 index 0000000000..3202c62e8a --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-sl/strings.xml @@ -0,0 +1,24 @@ + + + + + "Internetna povezava prek mobilnega telefona ni vzpostavljena" + "Napravi se ne moreta povezati" + "Izklopi internetno povezavo prek mobilnega telefona" + "Dostopna točka ali internetna povezava prek mobilnega telefona je vklopljena" + "Med gostovanjem lahko nastanejo dodatni stroški" + diff --git a/Tethering/res/values-mcc310-mnc004-sq/strings.xml b/Tethering/res/values-mcc310-mnc004-sq/strings.xml new file mode 100644 index 0000000000..37f6ad2868 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-sq/strings.xml @@ -0,0 +1,24 @@ + + + + + "Ndarja e internetit nuk ka internet" + "Pajisjet nuk mund të lidhen" + "Çaktivizo ndarjen e internetit" + "Zona e qasjes për internet ose ndarja e internetit është aktive" + "Mund të zbatohen tarifime shtesë kur je në roaming" + diff --git a/Tethering/res/values-mcc310-mnc004-sr/strings.xml b/Tethering/res/values-mcc310-mnc004-sr/strings.xml new file mode 100644 index 0000000000..5566d03ed1 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-sr/strings.xml @@ -0,0 +1,24 @@ + + + + + "Привезивање нема приступ интернету" + "Повезивање уређаја није успело" + "Искључи привезивање" + "Укључен је хотспот или привезивање" + "Можда важе додатни трошкови у ромингу" + diff --git a/Tethering/res/values-mcc310-mnc004-sv/strings.xml b/Tethering/res/values-mcc310-mnc004-sv/strings.xml new file mode 100644 index 0000000000..9765acd0cf --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-sv/strings.xml @@ -0,0 +1,24 @@ + + + + + "Det finns ingen internetanslutning för internetdelningen" + "Enheterna kan inte anslutas" + "Inaktivera internetdelning" + "Surfzon eller internetdelning har aktiverats" + "Ytterligare avgifter kan tillkomma vid roaming" + diff --git a/Tethering/res/values-mcc310-mnc004-sw/strings.xml b/Tethering/res/values-mcc310-mnc004-sw/strings.xml new file mode 100644 index 0000000000..cf850c9cd2 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-sw/strings.xml @@ -0,0 +1,24 @@ + + + + + "Kipengele cha kusambaza mtandao hakina intaneti" + "Imeshindwa kuunganisha vifaa" + "Zima kipengele cha kusambaza mtandao" + "Umewasha kipengele cha kusambaza mtandao au mtandao pepe" + "Huenda ukatozwa gharama za ziada ukitumia mitandao ya ng\'ambo" + diff --git a/Tethering/res/values-mcc310-mnc004-ta/strings.xml b/Tethering/res/values-mcc310-mnc004-ta/strings.xml new file mode 100644 index 0000000000..ea04821e33 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-ta/strings.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + "ரோமிங்கின்போது கூடுதல் கட்டணங்கள் விதிக்கப்படக்கூடும்" + diff --git a/Tethering/res/values-mcc310-mnc004-te/strings.xml b/Tethering/res/values-mcc310-mnc004-te/strings.xml new file mode 100644 index 0000000000..937d34d520 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-te/strings.xml @@ -0,0 +1,24 @@ + + + + + "టెథరింగ్ చేయడానికి ఇంటర్నెట్ కనెక్షన్ లేదు" + "పరికరాలు కనెక్ట్ అవ్వడం లేదు" + "టెథరింగ్‌ను ఆఫ్ చేయండి" + "హాట్‌స్పాట్ లేదా టెథరింగ్ ఆన్‌లో ఉంది" + "రోమింగ్‌లో ఉన్నప్పుడు అదనపు ఛార్జీలు వర్తించవచ్చు" + diff --git a/Tethering/res/values-mcc310-mnc004-th/strings.xml b/Tethering/res/values-mcc310-mnc004-th/strings.xml new file mode 100644 index 0000000000..f781fae525 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-th/strings.xml @@ -0,0 +1,24 @@ + + + + + "การเชื่อมต่ออินเทอร์เน็ตผ่านมือถือไม่มีอินเทอร์เน็ต" + "อุปกรณ์เชื่อมต่อไม่ได้" + "ปิดการเชื่อมต่ออินเทอร์เน็ตผ่านมือถือ" + "ฮอตสปอตหรือการเชื่อมต่ออินเทอร์เน็ตผ่านมือถือเปิดอยู่" + "อาจมีค่าใช้จ่ายเพิ่มเติมขณะโรมมิ่ง" + diff --git a/Tethering/res/values-mcc310-mnc004-tl/strings.xml b/Tethering/res/values-mcc310-mnc004-tl/strings.xml new file mode 100644 index 0000000000..8d5d465373 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-tl/strings.xml @@ -0,0 +1,24 @@ + + + + + "Walang internet ang pag-tether" + "Hindi makakonekta ang mga device" + "I-off ang pag-tether" + "Naka-on ang Hotspot o pag-tether" + "Posibleng magkaroon ng mga karagdagang singil habang nagro-roam" + diff --git a/Tethering/res/values-mcc310-mnc004-tr/strings.xml b/Tethering/res/values-mcc310-mnc004-tr/strings.xml new file mode 100644 index 0000000000..80cab33ac0 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-tr/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tethering\'in internet bağlantısı yok" + "Cihazlar bağlanamıyor" + "Tethering\'i kapat" + "Hotspot veya tethering açık" + "Dolaşım sırasında ek ücretler uygulanabilir" + diff --git a/Tethering/res/values-mcc310-mnc004-uk/strings.xml b/Tethering/res/values-mcc310-mnc004-uk/strings.xml new file mode 100644 index 0000000000..c05932a5ae --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-uk/strings.xml @@ -0,0 +1,24 @@ + + + + + "Телефон, який використовується як модем, не підключений до Інтернету" + "Не вдається підключити пристрої" + "Вимкнути використання телефона як модема" + "Увімкнено точку доступу або використання телефона як модема" + "У роумінгу може стягуватися додаткова плата" + diff --git a/Tethering/res/values-mcc310-mnc004-ur/strings.xml b/Tethering/res/values-mcc310-mnc004-ur/strings.xml new file mode 100644 index 0000000000..d820eee8ba --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-ur/strings.xml @@ -0,0 +1,24 @@ + + + + + "ٹیدرنگ میں انٹرنیٹ نہیں ہے" + "آلات منسلک نہیں ہو سکتے" + "ٹیدرنگ آف کریں" + "ہاٹ اسپاٹ یا ٹیدرنگ آن ہے" + "رومنگ کے دوران اضافی چارجز لاگو ہو سکتے ہیں" + diff --git a/Tethering/res/values-mcc310-mnc004-uz/strings.xml b/Tethering/res/values-mcc310-mnc004-uz/strings.xml new file mode 100644 index 0000000000..726148aaee --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-uz/strings.xml @@ -0,0 +1,24 @@ + + + + + "Modem internetga ulanmagan" + "Qurilmalar ulanmadi" + "Modem rejimini faolsizlantirish" + "Hotspot yoki modem rejimi yoniq" + "Rouming vaqtida qoʻshimcha haq olinishi mumkin" + diff --git a/Tethering/res/values-mcc310-mnc004-vi/strings.xml b/Tethering/res/values-mcc310-mnc004-vi/strings.xml new file mode 100644 index 0000000000..b7cb0456b6 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-vi/strings.xml @@ -0,0 +1,24 @@ + + + + + "Không có Internet để chia sẻ kết Internet" + "Các thiết bị không thể kết nối" + "Tắt tính năng chia sẻ Internet" + "Điểm phát sóng hoặc tính năng chia sẻ Internet đang bật" + "Bạn có thể mất thêm phí dữ liệu khi chuyển vùng" + diff --git a/Tethering/res/values-mcc310-mnc004-zh-rCN/strings.xml b/Tethering/res/values-mcc310-mnc004-zh-rCN/strings.xml new file mode 100644 index 0000000000..af91afff9a --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-zh-rCN/strings.xml @@ -0,0 +1,24 @@ + + + + + "共享网络未连接到互联网" + "设备无法连接" + "关闭网络共享" + "热点或网络共享已开启" + "漫游时可能会产生额外的费用" + diff --git a/Tethering/res/values-mcc310-mnc004-zh-rHK/strings.xml b/Tethering/res/values-mcc310-mnc004-zh-rHK/strings.xml new file mode 100644 index 0000000000..28e6b80c01 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-zh-rHK/strings.xml @@ -0,0 +1,24 @@ + + + + + "無法透過網絡共享連線至互聯網" + "裝置無法連接" + "關閉網絡共享" + "熱點或網絡共享已開啟" + "漫遊時可能需要支付額外費用" + diff --git a/Tethering/res/values-mcc310-mnc004-zh-rTW/strings.xml b/Tethering/res/values-mcc310-mnc004-zh-rTW/strings.xml new file mode 100644 index 0000000000..05b90692ea --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-zh-rTW/strings.xml @@ -0,0 +1,24 @@ + + + + + "無法透過數據連線連上網際網路" + "裝置無法連線" + "關閉數據連線" + "無線基地台或數據連線已開啟" + "使用漫遊服務可能須支付額外費用" + diff --git a/Tethering/res/values-mcc310-mnc004-zu/strings.xml b/Tethering/res/values-mcc310-mnc004-zu/strings.xml new file mode 100644 index 0000000000..11eb666219 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-zu/strings.xml @@ -0,0 +1,24 @@ + + + + + "Ukusebenzisa ifoni njengemodemu akunayo i-inthanethi" + "Amadivayisi awakwazi ukuxhumeka" + "Vala ukusebenzisa ifoni njengemodemu" + "I-hotspot noma ukusebenzisa ifoni njengemodemu kuvuliwe" + "Kungaba nezinkokhelo ezengeziwe uma uzula" + diff --git a/Tethering/res/values-mcc311-mnc480-af/strings.xml b/Tethering/res/values-mcc311-mnc480-af/strings.xml new file mode 100644 index 0000000000..9bfa5317a9 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-af/strings.xml @@ -0,0 +1,24 @@ + + + + + "Verbinding het nie internet nie" + "Toestelle kan nie koppel nie" + "Skakel verbinding af" + "Warmkol of verbinding is aan" + "Bykomende heffings kan geld terwyl jy swerf" + diff --git a/Tethering/res/values-mcc311-mnc480-am/strings.xml b/Tethering/res/values-mcc311-mnc480-am/strings.xml new file mode 100644 index 0000000000..5949dfa776 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-am/strings.xml @@ -0,0 +1,24 @@ + + + + + "ማስተሳሰር ምንም በይነመረብ የለውም" + "መሣሪያዎችን ማገናኘት አይቻልም" + "ማስተሳሰርን አጥፋ" + "መገናኛ ነጥብ ወይም ማስተሳሰር በርቷል" + "በሚያንዣብብበት ጊዜ ተጨማሪ ክፍያዎች ተፈጻሚ ሊሆኑ ይችላሉ" + diff --git a/Tethering/res/values-mcc311-mnc480-ar/strings.xml b/Tethering/res/values-mcc311-mnc480-ar/strings.xml new file mode 100644 index 0000000000..8467f9b1f5 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-ar/strings.xml @@ -0,0 +1,24 @@ + + + + + "ما مِن اتصال بالإنترنت خلال التوصيل" + "تعذّر اتصال الأجهزة" + "إيقاف التوصيل" + "نقطة الاتصال أو التوصيل مفعّلان" + "قد يتم تطبيق رسوم إضافية أثناء التجوال." + diff --git a/Tethering/res/values-mcc311-mnc480-as/strings.xml b/Tethering/res/values-mcc311-mnc480-as/strings.xml new file mode 100644 index 0000000000..9776bd89da --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-as/strings.xml @@ -0,0 +1,24 @@ + + + + + "টে\'ডাৰিঙৰ ইণ্টাৰনেট নাই" + "ডিভাইচসমূহ সংযোগ কৰিব নোৱাৰি" + "টে\'ডাৰিং অফ কৰক" + "হটস্পট অথবা টে\'ডাৰিং অন আছে" + "ৰ\'মিঙত থাকিলে অতিৰিক্ত মাচুল প্ৰযোজ্য হ’ব পাৰে" + diff --git a/Tethering/res/values-mcc311-mnc480-az/strings.xml b/Tethering/res/values-mcc311-mnc480-az/strings.xml new file mode 100644 index 0000000000..e6d3eaf9f0 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-az/strings.xml @@ -0,0 +1,24 @@ + + + + + "Modemin internetə girişi yoxdur" + "Cihazları qoşmaq mümkün deyil" + "Modemi deaktiv edin" + "Hotspot və ya modem aktivdir" + "Rouminq zamanı əlavə ödənişlər tətbiq edilə bilər" + diff --git a/Tethering/res/values-mcc311-mnc480-b+sr+Latn/strings.xml b/Tethering/res/values-mcc311-mnc480-b+sr+Latn/strings.xml new file mode 100644 index 0000000000..4c8a1df8ee --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-b+sr+Latn/strings.xml @@ -0,0 +1,24 @@ + + + + + "Privezivanje nema pristup internetu" + "Povezivanje uređaja nije uspelo" + "Isključi privezivanje" + "Uključen je hotspot ili privezivanje" + "Možda važe dodatni troškovi u romingu" + diff --git a/Tethering/res/values-mcc311-mnc480-be/strings.xml b/Tethering/res/values-mcc311-mnc480-be/strings.xml new file mode 100644 index 0000000000..edfa41e1ff --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-be/strings.xml @@ -0,0 +1,24 @@ + + + + + "Рэжым мадэма выкарыстоўваецца без доступу да інтэрнэту" + "Не ўдалося падключыць прылады" + "Выключыць рэжым мадэма" + "Хот-спот або рэжым мадэма ўключаны" + "Пры выкарыстанні роўмінгу можа спаганяцца дадатковая плата" + diff --git a/Tethering/res/values-mcc311-mnc480-bg/strings.xml b/Tethering/res/values-mcc311-mnc480-bg/strings.xml new file mode 100644 index 0000000000..f56398196f --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-bg/strings.xml @@ -0,0 +1,24 @@ + + + + + "Тетърингът няма връзка с интернет" + "Устройствата не могат да установят връзка" + "Изключване на тетъринга" + "Точката за достъп или тетърингът са включени" + "Възможно е да ви бъдат начислени допълнителни такси при роуминг" + diff --git a/Tethering/res/values-mcc311-mnc480-bn/strings.xml b/Tethering/res/values-mcc311-mnc480-bn/strings.xml new file mode 100644 index 0000000000..d8ecd2e988 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-bn/strings.xml @@ -0,0 +1,24 @@ + + + + + "টিথারিং করার জন্য কোনও ইন্টারনেট কানেকশন নেই" + "ডিভাইস কানেক্ট করতে পারছে না" + "টিথারিং বন্ধ করুন" + "হটস্পট বা টিথারিং চালু আছে" + "রোমিংয়ের সময় অতিরিক্ত চার্জ করা হতে পারে" + diff --git a/Tethering/res/values-mcc311-mnc480-bs/strings.xml b/Tethering/res/values-mcc311-mnc480-bs/strings.xml new file mode 100644 index 0000000000..b85fd5e285 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-bs/strings.xml @@ -0,0 +1,24 @@ + + + + + "Povezivanje putem mobitela nema internet" + "Uređaji se ne mogu povezati" + "Isključi povezivanje putem mobitela" + "Pristupna tačka ili povezivanje putem mobitela je uključeno" + "Mogu nastati dodatni troškovi u romingu" + diff --git a/Tethering/res/values-mcc311-mnc480-ca/strings.xml b/Tethering/res/values-mcc311-mnc480-ca/strings.xml new file mode 100644 index 0000000000..a3572151be --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-ca/strings.xml @@ -0,0 +1,24 @@ + + + + + "La compartició de xarxa no té accés a Internet" + "No es poden connectar els dispositius" + "Desactiva la compartició de xarxa" + "S\'ha activat el punt d\'accés Wi‑Fi o la compartició de xarxa" + "És possible que s\'apliquin costos addicionals en itinerància" + diff --git a/Tethering/res/values-mcc311-mnc480-cs/strings.xml b/Tethering/res/values-mcc311-mnc480-cs/strings.xml new file mode 100644 index 0000000000..91196be9e5 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-cs/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tethering nemá připojení k internetu" + "Zařízení se nemůžou připojit" + "Vypnout tethering" + "Je zapnutý hotspot nebo tethering" + "Při roamingu mohou být účtovány dodatečné poplatky" + diff --git a/Tethering/res/values-mcc311-mnc480-da/strings.xml b/Tethering/res/values-mcc311-mnc480-da/strings.xml new file mode 100644 index 0000000000..196890011d --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-da/strings.xml @@ -0,0 +1,24 @@ + + + + + "Netdeling har ingen internetforbindelse" + "Enheder kan ikke oprette forbindelse" + "Deaktiver netdeling" + "Hotspot eller netdeling er aktiveret" + "Der opkræves muligvis yderligere gebyrer ved roaming" + diff --git a/Tethering/res/values-mcc311-mnc480-de/strings.xml b/Tethering/res/values-mcc311-mnc480-de/strings.xml new file mode 100644 index 0000000000..eb3f8c52c0 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-de/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tethering hat keinen Internetzugriff" + "Geräte können sich nicht verbinden" + "Tethering deaktivieren" + "Hotspot oder Tethering ist aktiviert" + "Für das Roaming können zusätzliche Gebühren anfallen" + diff --git a/Tethering/res/values-mcc311-mnc480-el/strings.xml b/Tethering/res/values-mcc311-mnc480-el/strings.xml new file mode 100644 index 0000000000..56c3d81b63 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-el/strings.xml @@ -0,0 +1,24 @@ + + + + + "Η σύνδεση δεν έχει πρόσβαση στο διαδίκτυο" + "Δεν είναι δυνατή η σύνδεση των συσκευών" + "Απενεργοποιήστε τη σύνδεση" + "Ενεργό σημείο πρόσβασης Wi-Fi ή ενεργή σύνδεση" + "Ενδέχεται να ισχύουν επιπλέον χρεώσεις κατά την περιαγωγή." + diff --git a/Tethering/res/values-mcc311-mnc480-en-rAU/strings.xml b/Tethering/res/values-mcc311-mnc480-en-rAU/strings.xml new file mode 100644 index 0000000000..dd1a1971cd --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-en-rAU/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tethering has no Internet" + "Devices can’t connect" + "Turn off tethering" + "Hotspot or tethering is on" + "Additional charges may apply while roaming" + diff --git a/Tethering/res/values-mcc311-mnc480-en-rCA/strings.xml b/Tethering/res/values-mcc311-mnc480-en-rCA/strings.xml new file mode 100644 index 0000000000..dd1a1971cd --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-en-rCA/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tethering has no Internet" + "Devices can’t connect" + "Turn off tethering" + "Hotspot or tethering is on" + "Additional charges may apply while roaming" + diff --git a/Tethering/res/values-mcc311-mnc480-en-rGB/strings.xml b/Tethering/res/values-mcc311-mnc480-en-rGB/strings.xml new file mode 100644 index 0000000000..dd1a1971cd --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-en-rGB/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tethering has no Internet" + "Devices can’t connect" + "Turn off tethering" + "Hotspot or tethering is on" + "Additional charges may apply while roaming" + diff --git a/Tethering/res/values-mcc311-mnc480-en-rIN/strings.xml b/Tethering/res/values-mcc311-mnc480-en-rIN/strings.xml new file mode 100644 index 0000000000..dd1a1971cd --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-en-rIN/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tethering has no Internet" + "Devices can’t connect" + "Turn off tethering" + "Hotspot or tethering is on" + "Additional charges may apply while roaming" + diff --git a/Tethering/res/values-mcc311-mnc480-en-rXC/strings.xml b/Tethering/res/values-mcc311-mnc480-en-rXC/strings.xml new file mode 100644 index 0000000000..d3347aae20 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-en-rXC/strings.xml @@ -0,0 +1,24 @@ + + + + + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‎‎‎‎‎‏‎‎‏‏‏‏‎‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‎‏‎‏‏‎‏‏‏‎‎‏‎‏‎‎‎‏‎‎‎Tethering has no internet‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‏‎‏‎‎‏‎‎‏‏‏‎‏‏‎‏‎‏‎‏‎‎‎‏‎‎‎‎‎‏‏‏‏‎‏‎‎‎‎‏‎‏‏‎‏‏‎‏‎‎‏‏‏‏‏‎Devices can’t connect‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‎‏‏‎‎‏‏‏‎‏‎‏‎‎‎‏‏‏‎‎‏‏‏‏‎‎‏‏‏‏‏‏‎‎‎‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‎Turn off tethering‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‎‏‏‏‎‎‏‎‎‏‎‏‎‏‏‏‎‏‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‏‎‏‎‎‎‏‎‎‎‎‏‏‎Hotspot or tethering is on‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‏‏‎‏‎‏‎‏‎‎‎‏‎‏‎‏‏‎‏‎‎‎‏‏‏‏‎‎‏‏‏‏‎‎‎‏‎‎‎‎‏‏‎‏‎‏‎‎‏‏‎‎‏‏‎Additional charges may apply while roaming‎‏‎‎‏‎" + diff --git a/Tethering/res/values-mcc311-mnc480-es-rUS/strings.xml b/Tethering/res/values-mcc311-mnc480-es-rUS/strings.xml new file mode 100644 index 0000000000..2f0504f07d --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-es-rUS/strings.xml @@ -0,0 +1,24 @@ + + + + + "La conexión mediante dispositivo móvil no tiene Internet" + "No se pueden conectar los dispositivos" + "Desactivar conexión mediante dispositivo móvil" + "Se activó el hotspot o la conexión mediante dispositivo móvil" + "Es posible que se apliquen cargos adicionales por roaming" + diff --git a/Tethering/res/values-mcc311-mnc480-es/strings.xml b/Tethering/res/values-mcc311-mnc480-es/strings.xml new file mode 100644 index 0000000000..2d8f882425 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-es/strings.xml @@ -0,0 +1,24 @@ + + + + + "La conexión no se puede compartir, porque no hay acceso a Internet" + "Los dispositivos no se pueden conectar" + "Desactivar conexión compartida" + "Punto de acceso o conexión compartida activados" + "Puede que se apliquen cargos adicionales en itinerancia" + diff --git a/Tethering/res/values-mcc311-mnc480-et/strings.xml b/Tethering/res/values-mcc311-mnc480-et/strings.xml new file mode 100644 index 0000000000..8493c47071 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-et/strings.xml @@ -0,0 +1,24 @@ + + + + + "Jagamisel puudub internetiühendus" + "Seadmed ei saa ühendust luua" + "Lülita jagamine välja" + "Kuumkoht või jagamine on sisse lülitatud" + "Rändluse kasutamisega võivad kaasneda lisatasud" + diff --git a/Tethering/res/values-mcc311-mnc480-eu/strings.xml b/Tethering/res/values-mcc311-mnc480-eu/strings.xml new file mode 100644 index 0000000000..33bccab3e8 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-eu/strings.xml @@ -0,0 +1,24 @@ + + + + + "Konexioa partekatzeko aukerak ez du Interneteko konexiorik" + "Ezin dira konektatu gailuak" + "Desaktibatu konexioa partekatzeko aukera" + "Wifi-gunea edo konexioa partekatzeko aukera aktibatuta dago" + "Baliteke kostu gehigarriak ordaindu behar izatea ibiltaritzan" + diff --git a/Tethering/res/values-mcc311-mnc480-fa/strings.xml b/Tethering/res/values-mcc311-mnc480-fa/strings.xml new file mode 100644 index 0000000000..cf8a0cc277 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-fa/strings.xml @@ -0,0 +1,24 @@ + + + + + "«اشتراک‌گذاری اینترنت» به اینترنت دسترسی ندارد" + "دستگاه‌ها متصل نمی‌شوند" + "خاموش کردن «اشتراک‌گذاری اینترنت»" + "«نقطه اتصال» یا «اشتراک‌گذاری اینترنت» روشن است" + "ممکن است درحین فراگردی تغییرات دیگر اعمال شود" + diff --git a/Tethering/res/values-mcc311-mnc480-fi/strings.xml b/Tethering/res/values-mcc311-mnc480-fi/strings.xml new file mode 100644 index 0000000000..6a3ab806db --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-fi/strings.xml @@ -0,0 +1,24 @@ + + + + + "Ei jaettavaa internetyhteyttä" + "Laitteet eivät voi muodostaa yhteyttä" + "Laita yhteyden jakaminen pois päältä" + "Hotspot tai yhteyden jakaminen on päällä" + "Roaming voi aiheuttaa lisämaksuja" + diff --git a/Tethering/res/values-mcc311-mnc480-fr-rCA/strings.xml b/Tethering/res/values-mcc311-mnc480-fr-rCA/strings.xml new file mode 100644 index 0000000000..ffb9bf6047 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-fr-rCA/strings.xml @@ -0,0 +1,24 @@ + + + + + "Le partage de connexion n\'est pas connecté à Internet" + "Impossible de connecter les appareils" + "Désactiver le partage de connexion" + "Le point d\'accès ou le partage de connexion est activé" + "En itinérance, des frais supplémentaires peuvent s\'appliquer" + diff --git a/Tethering/res/values-mcc311-mnc480-fr/strings.xml b/Tethering/res/values-mcc311-mnc480-fr/strings.xml new file mode 100644 index 0000000000..768bce3f0a --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-fr/strings.xml @@ -0,0 +1,24 @@ + + + + + "Aucune connexion à Internet n\'est disponible pour le partage de connexion" + "Impossible de connecter les appareils" + "Désactiver le partage de connexion" + "Le point d\'accès ou le partage de connexion est activé" + "En itinérance, des frais supplémentaires peuvent s\'appliquer" + diff --git a/Tethering/res/values-mcc311-mnc480-gl/strings.xml b/Tethering/res/values-mcc311-mnc480-gl/strings.xml new file mode 100644 index 0000000000..0c4195a7ca --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-gl/strings.xml @@ -0,0 +1,24 @@ + + + + + "A conexión compartida non ten Internet" + "Non se puideron conectar os dispositivos" + "Desactivar conexión compartida" + "Está activada a zona wifi ou a conexión compartida" + "Pódense aplicar cargos adicionais en itinerancia" + diff --git a/Tethering/res/values-mcc311-mnc480-gu/strings.xml b/Tethering/res/values-mcc311-mnc480-gu/strings.xml new file mode 100644 index 0000000000..e9d33a7db2 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-gu/strings.xml @@ -0,0 +1,24 @@ + + + + + "ઇન્ટરનેટ શેર કરવાની સુવિધામાં ઇન્ટરનેટ નથી" + "ડિવાઇસ કનેક્ટ કરી શકાતા નથી" + "ઇન્ટરનેટ શેર કરવાની સુવિધા બંધ કરો" + "હૉટસ્પૉટ અથવા ઇન્ટરનેટ શેર કરવાની સુવિધા ચાલુ છે" + "રોમિંગમાં વધારાના શુલ્ક લાગી શકે છે" + diff --git a/Tethering/res/values-mcc311-mnc480-hi/strings.xml b/Tethering/res/values-mcc311-mnc480-hi/strings.xml new file mode 100644 index 0000000000..aa418ac5d3 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-hi/strings.xml @@ -0,0 +1,24 @@ + + + + + "टेदरिंग से इंटरनेट नहीं चल रहा" + "डिवाइस कनेक्ट नहीं हो पा रहे" + "टेदरिंग बंद करें" + "हॉटस्पॉट या टेदरिंग चालू है" + "रोमिंग के दौरान अतिरिक्त शुल्क लग सकता है" + diff --git a/Tethering/res/values-mcc311-mnc480-hr/strings.xml b/Tethering/res/values-mcc311-mnc480-hr/strings.xml new file mode 100644 index 0000000000..51c524afbc --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-hr/strings.xml @@ -0,0 +1,24 @@ + + + + + "Modemsko povezivanje nema internet" + "Uređaji se ne mogu povezati" + "Isključivanje modemskog povezivanja" + "Uključena je žarišna točka ili modemsko povezivanje" + "U roamingu su mogući dodatni troškovi" + diff --git a/Tethering/res/values-mcc311-mnc480-hu/strings.xml b/Tethering/res/values-mcc311-mnc480-hu/strings.xml new file mode 100644 index 0000000000..164e45edd1 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-hu/strings.xml @@ -0,0 +1,24 @@ + + + + + "Nincs internetkapcsolat az internet megosztásához" + "Az eszközök nem tudnak csatlakozni" + "Internetmegosztás kikapcsolása" + "A hotspot vagy az internetmegosztás be van kapcsolva" + "Roaming során további díjak léphetnek fel" + diff --git a/Tethering/res/values-mcc311-mnc480-hy/strings.xml b/Tethering/res/values-mcc311-mnc480-hy/strings.xml new file mode 100644 index 0000000000..e76c0a4c80 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-hy/strings.xml @@ -0,0 +1,24 @@ + + + + + "Մոդեմի ռեժիմի կապը բացակայում է" + "Չհաջողվեց միացնել սարքը" + "Անջատել մոդեմի ռեժիմը" + "Թեժ կետը կամ մոդեմի ռեժիմը միացված է" + "Ռոումինգում կարող են լրացուցիչ վճարներ գանձվել" + diff --git a/Tethering/res/values-mcc311-mnc480-in/strings.xml b/Tethering/res/values-mcc311-mnc480-in/strings.xml new file mode 100644 index 0000000000..2b817f8abd --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-in/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tidak ada koneksi internet di tethering" + "Perangkat tidak dapat terhubung" + "Nonaktifkan tethering" + "Hotspot atau tethering aktif" + "Biaya tambahan mungkin berlaku saat roaming" + diff --git a/Tethering/res/values-mcc311-mnc480-is/strings.xml b/Tethering/res/values-mcc311-mnc480-is/strings.xml new file mode 100644 index 0000000000..a338d9c7ca --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-is/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tjóðrun er ekki með internettengingu" + "Tæki geta ekki tengst" + "Slökkva á tjóðrun" + "Kveikt er á heitum reit eða tjóðrun" + "Viðbótargjöld kunna að eiga við í reiki" + diff --git a/Tethering/res/values-mcc311-mnc480-it/strings.xml b/Tethering/res/values-mcc311-mnc480-it/strings.xml new file mode 100644 index 0000000000..77769c2ac5 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-it/strings.xml @@ -0,0 +1,24 @@ + + + + + "Nessuna connessione a Internet per il tethering" + "Impossibile connettere i dispositivi" + "Disattiva il tethering" + "Hotspot o tethering attivi" + "Potrebbero essere applicati costi aggiuntivi durante il roaming" + diff --git a/Tethering/res/values-mcc311-mnc480-iw/strings.xml b/Tethering/res/values-mcc311-mnc480-iw/strings.xml new file mode 100644 index 0000000000..5267b51264 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-iw/strings.xml @@ -0,0 +1,24 @@ + + + + + "אי אפשר להפעיל את תכונת שיתוף האינטרנט בין מכשירים כי אין חיבור לאינטרנט" + "למכשירים אין אפשרות להתחבר" + "השבתה של שיתוף האינטרנט בין מכשירים" + "תכונת הנקודה לשיתוף אינטרנט או תכונת שיתוף האינטרנט בין מכשירים פועלת" + "ייתכנו חיובים נוספים בעת נדידה" + diff --git a/Tethering/res/values-mcc311-mnc480-ja/strings.xml b/Tethering/res/values-mcc311-mnc480-ja/strings.xml new file mode 100644 index 0000000000..66a9a6dd35 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-ja/strings.xml @@ -0,0 +1,24 @@ + + + + + "テザリングがインターネットに接続されていません" + "デバイスを接続できません" + "テザリングを OFF にする" + "アクセス ポイントまたはテザリングが ON です" + "ローミング時に追加料金が発生することがあります" + diff --git a/Tethering/res/values-mcc311-mnc480-ka/strings.xml b/Tethering/res/values-mcc311-mnc480-ka/strings.xml new file mode 100644 index 0000000000..d8ad880849 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-ka/strings.xml @@ -0,0 +1,24 @@ + + + + + "ტეტერინგს არ აქვს ინტერნეტზე წვდომა" + "მოწყობილობები ვერ ახერხებენ დაკავშირებას" + "ტეტერინგის გამორთვა" + "ჩართულია უსადენო ქსელი ან ტეტერინგი" + "როუმინგის გამოყენებისას შეიძლება ჩამოგეჭრათ დამატებითი საფასური" + diff --git a/Tethering/res/values-mcc311-mnc480-kk/strings.xml b/Tethering/res/values-mcc311-mnc480-kk/strings.xml new file mode 100644 index 0000000000..1ddd6b419b --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-kk/strings.xml @@ -0,0 +1,24 @@ + + + + + "Тетеринг режимі интернет байланысынсыз пайдаланылуда" + "Құрылғыларды байланыстыру мүмкін емес" + "Тетерингіні өшіру" + "Хотспот немесе тетеринг қосулы" + "Роуминг кезінде қосымша ақы алынуы мүмкін." + diff --git a/Tethering/res/values-mcc311-mnc480-km/strings.xml b/Tethering/res/values-mcc311-mnc480-km/strings.xml new file mode 100644 index 0000000000..cf5a1379cc --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-km/strings.xml @@ -0,0 +1,24 @@ + + + + + "ការភ្ជាប់​មិនមានអ៊ីនធឺណិត​ទេ" + "មិនអាច​ភ្ជាប់ឧបករណ៍​បានទេ" + "បិទការភ្ជាប់" + "ហតស្ប៉ត ឬការភ្ជាប់​ត្រូវបានបើក" + "អាចមាន​ការគិតថ្លៃ​បន្ថែម នៅពេល​រ៉ូមីង" + diff --git a/Tethering/res/values-mcc311-mnc480-kn/strings.xml b/Tethering/res/values-mcc311-mnc480-kn/strings.xml new file mode 100644 index 0000000000..68ae68bc19 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-kn/strings.xml @@ -0,0 +1,24 @@ + + + + + "ಟೆಥರಿಂಗ್‌ ಯಾವುದೇ ಇಂಟರ್ನೆಟ್ ಕನೆಕ್ಷನ್ ಹೊಂದಿಲ್ಲ" + "ಸಾಧನಗಳನ್ನು ಕನೆಕ್ಟ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ" + "ಟೆಥರಿಂಗ್‌ ಆಫ್ ಮಾಡಿ" + "ಹಾಟ್‌ಸ್ಪಾಟ್ ಅಥವಾ ಟೆಥರಿಂಗ್‌ ಆನ್ ಆಗಿದೆ" + "ರೋಮಿಂಗ್‌ನಲ್ಲಿರುವಾಗ ಹೆಚ್ಚುವರಿ ಶುಲ್ಕಗಳು ಅನ್ವಯವಾಗಬಹುದು" + diff --git a/Tethering/res/values-mcc311-mnc480-ko/strings.xml b/Tethering/res/values-mcc311-mnc480-ko/strings.xml new file mode 100644 index 0000000000..17185ba2d0 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-ko/strings.xml @@ -0,0 +1,24 @@ + + + + + "테더링으로 인터넷을 사용할 수 없음" + "기기에서 연결할 수 없음" + "테더링 사용 중지" + "핫스팟 또는 테더링 켜짐" + "로밍 중에는 추가 요금이 발생할 수 있습니다." + diff --git a/Tethering/res/values-mcc311-mnc480-ky/strings.xml b/Tethering/res/values-mcc311-mnc480-ky/strings.xml new file mode 100644 index 0000000000..6a9fb9810c --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-ky/strings.xml @@ -0,0 +1,24 @@ + + + + + "Модем режими Интернети жок колдонулууда" + "Түзмөктөр туташпай жатат" + "Модем режимин өчүрүү" + "Байланыш түйүнү же модем режими күйүк" + "Роумингде кошумча акы алынышы мүмкүн" + diff --git a/Tethering/res/values-mcc311-mnc480-lo/strings.xml b/Tethering/res/values-mcc311-mnc480-lo/strings.xml new file mode 100644 index 0000000000..bcc4b57626 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-lo/strings.xml @@ -0,0 +1,24 @@ + + + + + "ການປ່ອຍສັນຍານບໍ່ມີອິນເຕີເນັດ" + "ອຸປະກອນບໍ່ສາມາດເຊື່ອມຕໍ່ໄດ້" + "ປິດການປ່ອຍສັນຍານ" + "ເປີດໃຊ້ຮັອດສະປອດ ຫຼື ການປ່ອຍສັນຍານຢູ່" + "ອາດມີຄ່າໃຊ້ຈ່າຍເພີ່ມເຕີມໃນລະຫວ່າງການໂຣມມິງ" + diff --git a/Tethering/res/values-mcc311-mnc480-lt/strings.xml b/Tethering/res/values-mcc311-mnc480-lt/strings.xml new file mode 100644 index 0000000000..011c2c11fb --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-lt/strings.xml @@ -0,0 +1,24 @@ + + + + + "Nėra įrenginio kaip modemo naudojimo interneto ryšio" + "Nepavyko susieti įrenginių" + "Išjungti įrenginio kaip modemo naudojimą" + "Įjungtas viešosios interneto prieigos taškas arba įrenginio kaip modemo naudojimas" + "Veikiant tarptinkliniam ryšiui gali būti taikomi papildomi mokesčiai" + diff --git a/Tethering/res/values-mcc311-mnc480-lv/strings.xml b/Tethering/res/values-mcc311-mnc480-lv/strings.xml new file mode 100644 index 0000000000..5cb2f3b7aa --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-lv/strings.xml @@ -0,0 +1,24 @@ + + + + + "Piesaistei nav interneta savienojuma" + "Nevar savienot ierīces" + "Izslēgt piesaisti" + "Ir ieslēgts tīklājs vai piesaiste" + "Viesabonēšanas laikā var tikt piemērota papildu samaksa" + diff --git a/Tethering/res/values-mcc311-mnc480-mk/strings.xml b/Tethering/res/values-mcc311-mnc480-mk/strings.xml new file mode 100644 index 0000000000..4cbfd887c5 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-mk/strings.xml @@ -0,0 +1,24 @@ + + + + + "Нема интернет преку мобилен" + "Уредите не може да се поврзат" + "Исклучи интернет преку мобилен" + "Точката на пристап или интернетот преку мобилен е вклучен" + "При роаминг може да се наплатат дополнителни трошоци" + diff --git a/Tethering/res/values-mcc311-mnc480-ml/strings.xml b/Tethering/res/values-mcc311-mnc480-ml/strings.xml new file mode 100644 index 0000000000..9cf4eaf34a --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-ml/strings.xml @@ -0,0 +1,24 @@ + + + + + "ടെതറിംഗിന് ഇന്റർനെറ്റ് ഇല്ല" + "ഉപകരണങ്ങൾ കണക്റ്റ് ചെയ്യാനാവില്ല" + "ടെതറിംഗ് ഓഫാക്കുക" + "ഹോട്ട്‌സ്‌പോട്ട് അല്ലെങ്കിൽ ടെതറിംഗ് ഓണാണ്" + "റോമിംഗ് ചെയ്യുമ്പോൾ അധിക നിരക്കുകൾ ബാധകമായേക്കാം" + diff --git a/Tethering/res/values-mcc311-mnc480-mn/strings.xml b/Tethering/res/values-mcc311-mnc480-mn/strings.xml new file mode 100644 index 0000000000..47c82c14d9 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-mn/strings.xml @@ -0,0 +1,24 @@ + + + + + "Модемд интернэт алга байна" + "Төхөөрөмжүүд холбогдох боломжгүй байна" + "Модем болгохыг унтраах" + "Сүлжээний цэг эсвэл модем болгох асаалттай байна" + "Роумингийн үеэр нэмэлт төлбөр нэхэмжилж болзошгүй" + diff --git a/Tethering/res/values-mcc311-mnc480-mr/strings.xml b/Tethering/res/values-mcc311-mnc480-mr/strings.xml new file mode 100644 index 0000000000..ad9e809ab2 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-mr/strings.xml @@ -0,0 +1,24 @@ + + + + + "टेदरिंगला इंटरनेट नाही" + "डिव्हाइस कनेक्ट होऊ शकत नाहीत" + "टेदरिंग बंद करा" + "हॉटस्पॉट किंवा टेदरिंग सुरू आहे" + "रोमिंगदरम्यान अतिरिक्त शुल्क लागू होऊ शकतात" + diff --git a/Tethering/res/values-mcc311-mnc480-ms/strings.xml b/Tethering/res/values-mcc311-mnc480-ms/strings.xml new file mode 100644 index 0000000000..e708cb8717 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-ms/strings.xml @@ -0,0 +1,24 @@ + + + + + "Penambatan tiada Internet" + "Peranti tidak dapat disambungkan" + "Matikan penambatan" + "Tempat liputan atau penambatan dihidupkan" + "Caj tambahan mungkin digunakan semasa perayauan" + diff --git a/Tethering/res/values-mcc311-mnc480-my/strings.xml b/Tethering/res/values-mcc311-mnc480-my/strings.xml new file mode 100644 index 0000000000..ba5462250b --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-my/strings.xml @@ -0,0 +1,24 @@ + + + + + "မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်းတွင် အင်တာနက် မရှိပါ" + "စက်များ ချိတ်ဆက်၍ မရပါ" + "မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်း ပိတ်ရန်" + "ဟော့စပေါ့ (သို့) မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်း ဖွင့်ထားသည်" + "ပြင်ပကွန်ရက်နှင့် ချိတ်ဆက်သည့်အခါ နောက်ထပ်ကျသင့်မှုများ ရှိနိုင်သည်" + diff --git a/Tethering/res/values-mcc311-mnc480-nb/strings.xml b/Tethering/res/values-mcc311-mnc480-nb/strings.xml new file mode 100644 index 0000000000..57db484a25 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-nb/strings.xml @@ -0,0 +1,24 @@ + + + + + "Internettdeling har ikke internettilgang" + "Enhetene kan ikke koble til" + "Slå av internettdeling" + "Wi-Fi-sone eller internettdeling er på" + "Ytterligere kostnader kan påløpe under roaming" + diff --git a/Tethering/res/values-mcc311-mnc480-ne/strings.xml b/Tethering/res/values-mcc311-mnc480-ne/strings.xml new file mode 100644 index 0000000000..617c50dd0c --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-ne/strings.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + "रोमिङ सेवा प्रयोग गर्दा अतिरिक्त शुल्क लाग्न सक्छ" + diff --git a/Tethering/res/values-mcc311-mnc480-nl/strings.xml b/Tethering/res/values-mcc311-mnc480-nl/strings.xml new file mode 100644 index 0000000000..b08133f4e5 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-nl/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tethering heeft geen internet" + "Apparaten kunnen niet worden verbonden" + "Tethering uitschakelen" + "Hotspot of tethering is ingeschakeld" + "Er kunnen extra kosten voor roaming in rekening worden gebracht." + diff --git a/Tethering/res/values-mcc311-mnc480-or/strings.xml b/Tethering/res/values-mcc311-mnc480-or/strings.xml new file mode 100644 index 0000000000..1ad4ca354a --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-or/strings.xml @@ -0,0 +1,24 @@ + + + + + "ଟିଥରିଂ ପାଇଁ କୌଣସି ଇଣ୍ଟର୍ନେଟ୍ ସଂଯୋଗ ନାହିଁ" + "ଡିଭାଇସଗୁଡ଼ିକ ସଂଯୋଗ କରାଯାଇପାରିବ ନାହିଁ" + "ଟିଥରିଂ ବନ୍ଦ କରନ୍ତୁ" + "ହଟସ୍ପଟ୍ କିମ୍ବା ଟିଥରିଂ ଚାଲୁ ଅଛି" + "ରୋମିଂରେ ଥିବା ସମୟରେ ଅତିରିକ୍ତ ଶୁଳ୍କ ଲାଗୁ ହୋଇପାରେ" + diff --git a/Tethering/res/values-mcc311-mnc480-pa/strings.xml b/Tethering/res/values-mcc311-mnc480-pa/strings.xml new file mode 100644 index 0000000000..88def563d8 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-pa/strings.xml @@ -0,0 +1,24 @@ + + + + + "ਟੈਦਰਿੰਗ ਕੋਲ ਇੰਟਰਨੈੱਟ ਪਹੁੰਚ ਨਹੀਂ ਹੈ" + "ਡੀਵਾਈਸ ਕਨੈਕਟ ਨਹੀਂ ਕੀਤੇ ਜਾ ਸਕਦੇ" + "ਟੈਦਰਿੰਗ ਬੰਦ ਕਰੋ" + "ਹੌਟਸਪੌਟ ਜਾਂ ਟੈਦਰਿੰਗ ਚਾਲੂ ਹੈ" + "ਰੋਮਿੰਗ ਦੌਰਾਨ ਵਧੀਕ ਖਰਚੇ ਲਾਗੂ ਹੋ ਸਕਦੇ ਹਨ" + diff --git a/Tethering/res/values-mcc311-mnc480-pl/strings.xml b/Tethering/res/values-mcc311-mnc480-pl/strings.xml new file mode 100644 index 0000000000..f9890abdc2 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-pl/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tethering nie ma internetu" + "Urządzenia nie mogą się połączyć" + "Wyłącz tethering" + "Hotspot lub tethering jest włączony" + "Podczas korzystania z roamingu mogą zostać naliczone dodatkowe opłaty" + diff --git a/Tethering/res/values-mcc311-mnc480-pt-rBR/strings.xml b/Tethering/res/values-mcc311-mnc480-pt-rBR/strings.xml new file mode 100644 index 0000000000..ce3b88479f --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-pt-rBR/strings.xml @@ -0,0 +1,24 @@ + + + + + "O tethering não tem Internet" + "Não é possível conectar os dispositivos" + "Desativar o tethering" + "Ponto de acesso ou tethering ativado" + "Pode haver cobranças extras durante o roaming" + diff --git a/Tethering/res/values-mcc311-mnc480-pt-rPT/strings.xml b/Tethering/res/values-mcc311-mnc480-pt-rPT/strings.xml new file mode 100644 index 0000000000..7e883ea576 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-pt-rPT/strings.xml @@ -0,0 +1,24 @@ + + + + + "A ligação (à Internet) via telemóvel não tem Internet" + "Não é possível ligar os dispositivos" + "Desativar ligação (à Internet) via telemóvel" + "A zona Wi-Fi ou a ligação (à Internet) via telemóvel está ativada" + "Podem aplicar-se custos adicionais em roaming." + diff --git a/Tethering/res/values-mcc311-mnc480-pt/strings.xml b/Tethering/res/values-mcc311-mnc480-pt/strings.xml new file mode 100644 index 0000000000..ce3b88479f --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-pt/strings.xml @@ -0,0 +1,24 @@ + + + + + "O tethering não tem Internet" + "Não é possível conectar os dispositivos" + "Desativar o tethering" + "Ponto de acesso ou tethering ativado" + "Pode haver cobranças extras durante o roaming" + diff --git a/Tethering/res/values-mcc311-mnc480-ro/strings.xml b/Tethering/res/values-mcc311-mnc480-ro/strings.xml new file mode 100644 index 0000000000..1009417316 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-ro/strings.xml @@ -0,0 +1,24 @@ + + + + + "Procesul de tethering nu are internet" + "Dispozitivele nu se pot conecta" + "Dezactivați procesul de tethering" + "S-a activat hotspotul sau tethering" + "Se pot aplica taxe suplimentare pentru roaming" + diff --git a/Tethering/res/values-mcc311-mnc480-ru/strings.xml b/Tethering/res/values-mcc311-mnc480-ru/strings.xml new file mode 100644 index 0000000000..88683bed95 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-ru/strings.xml @@ -0,0 +1,24 @@ + + + + + "Режим модема используется без доступа к Интернету" + "Невозможно подключить устройства." + "Отключить режим модема" + "Включены точка доступа или режим модема" + "За использование услуг связи в роуминге может взиматься дополнительная плата." + diff --git a/Tethering/res/values-mcc311-mnc480-si/strings.xml b/Tethering/res/values-mcc311-mnc480-si/strings.xml new file mode 100644 index 0000000000..176bcdb797 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-si/strings.xml @@ -0,0 +1,24 @@ + + + + + "ටෙදරින් හට අන්තර්ජාලය නැත" + "උපාංගවලට සම්බන්ධ විය නොහැකිය" + "ටෙදරින් ක්‍රියාවිරහිත කරන්න" + "හොට්ස්පොට් හෝ ටෙදරින් ක්‍රියාත්මකයි" + "රෝමිං අතරතුර අමතර ගාස්තු අදාළ විය හැකිය" + diff --git a/Tethering/res/values-mcc311-mnc480-sk/strings.xml b/Tethering/res/values-mcc311-mnc480-sk/strings.xml new file mode 100644 index 0000000000..b9e2127fa8 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-sk/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tethering nemá internetové pripojenie" + "Zariadenia sa nemôžu pripojiť" + "Vypnúť tethering" + "Je zapnutý hotspot alebo tethering" + "Počas roamingu vám môžu byť účtované ďalšie poplatky" + diff --git a/Tethering/res/values-mcc311-mnc480-sl/strings.xml b/Tethering/res/values-mcc311-mnc480-sl/strings.xml new file mode 100644 index 0000000000..e8140e686a --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-sl/strings.xml @@ -0,0 +1,24 @@ + + + + + "Internetna povezava prek mobilnega telefona ni vzpostavljena" + "Napravi se ne moreta povezati" + "Izklopi internetno povezavo prek mobilnega telefona" + "Dostopna točka ali internetna povezava prek mobilnega telefona je vklopljena" + "Med gostovanjem lahko nastanejo dodatni stroški" + diff --git a/Tethering/res/values-mcc311-mnc480-sq/strings.xml b/Tethering/res/values-mcc311-mnc480-sq/strings.xml new file mode 100644 index 0000000000..61e698d6e8 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-sq/strings.xml @@ -0,0 +1,24 @@ + + + + + "Ndarja e internetit nuk ka internet" + "Pajisjet nuk mund të lidhen" + "Çaktivizo ndarjen e internetit" + "Zona e qasjes për internet ose ndarja e internetit është aktive" + "Mund të zbatohen tarifime shtesë kur je në roaming" + diff --git a/Tethering/res/values-mcc311-mnc480-sr/strings.xml b/Tethering/res/values-mcc311-mnc480-sr/strings.xml new file mode 100644 index 0000000000..b4c411c354 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-sr/strings.xml @@ -0,0 +1,24 @@ + + + + + "Привезивање нема приступ интернету" + "Повезивање уређаја није успело" + "Искључи привезивање" + "Укључен је хотспот или привезивање" + "Можда важе додатни трошкови у ромингу" + diff --git a/Tethering/res/values-mcc311-mnc480-sv/strings.xml b/Tethering/res/values-mcc311-mnc480-sv/strings.xml new file mode 100644 index 0000000000..4f543e47b9 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-sv/strings.xml @@ -0,0 +1,24 @@ + + + + + "Det finns ingen internetanslutning för internetdelningen" + "Enheterna kan inte anslutas" + "Inaktivera internetdelning" + "Surfzon eller internetdelning har aktiverats" + "Ytterligare avgifter kan tillkomma vid roaming" + diff --git a/Tethering/res/values-mcc311-mnc480-sw/strings.xml b/Tethering/res/values-mcc311-mnc480-sw/strings.xml new file mode 100644 index 0000000000..ac347ab485 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-sw/strings.xml @@ -0,0 +1,24 @@ + + + + + "Kipengele cha kusambaza mtandao hakina intaneti" + "Imeshindwa kuunganisha vifaa" + "Zima kipengele cha kusambaza mtandao" + "Umewasha kipengele cha kusambaza mtandao au mtandao pepe" + "Huenda ukatozwa gharama za ziada ukitumia mitandao ya ng\'ambo" + diff --git a/Tethering/res/values-mcc311-mnc480-ta/strings.xml b/Tethering/res/values-mcc311-mnc480-ta/strings.xml new file mode 100644 index 0000000000..0e437593ee --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-ta/strings.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + "ரோமிங்கின்போது கூடுதல் கட்டணங்கள் விதிக்கப்படக்கூடும்" + diff --git a/Tethering/res/values-mcc311-mnc480-te/strings.xml b/Tethering/res/values-mcc311-mnc480-te/strings.xml new file mode 100644 index 0000000000..9360297dd8 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-te/strings.xml @@ -0,0 +1,24 @@ + + + + + "టెథరింగ్ చేయడానికి ఇంటర్నెట్ కనెక్షన్ లేదు" + "పరికరాలు కనెక్ట్ అవ్వడం లేదు" + "టెథరింగ్‌ను ఆఫ్ చేయండి" + "హాట్‌స్పాట్ లేదా టెథరింగ్ ఆన్‌లో ఉంది" + "రోమింగ్‌లో ఉన్నప్పుడు అదనపు ఛార్జీలు వర్తించవచ్చు" + diff --git a/Tethering/res/values-mcc311-mnc480-th/strings.xml b/Tethering/res/values-mcc311-mnc480-th/strings.xml new file mode 100644 index 0000000000..9c4d7e08f2 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-th/strings.xml @@ -0,0 +1,24 @@ + + + + + "การเชื่อมต่ออินเทอร์เน็ตผ่านมือถือไม่มีอินเทอร์เน็ต" + "อุปกรณ์เชื่อมต่อไม่ได้" + "ปิดการเชื่อมต่ออินเทอร์เน็ตผ่านมือถือ" + "ฮอตสปอตหรือการเชื่อมต่ออินเทอร์เน็ตผ่านมือถือเปิดอยู่" + "อาจมีค่าใช้จ่ายเพิ่มเติมขณะโรมมิ่ง" + diff --git a/Tethering/res/values-mcc311-mnc480-tl/strings.xml b/Tethering/res/values-mcc311-mnc480-tl/strings.xml new file mode 100644 index 0000000000..a7c78a5932 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-tl/strings.xml @@ -0,0 +1,24 @@ + + + + + "Walang internet ang pag-tether" + "Hindi makakonekta ang mga device" + "I-off ang pag-tether" + "Naka-on ang Hotspot o pag-tether" + "Posibleng magkaroon ng mga karagdagang singil habang nagro-roam" + diff --git a/Tethering/res/values-mcc311-mnc480-tr/strings.xml b/Tethering/res/values-mcc311-mnc480-tr/strings.xml new file mode 100644 index 0000000000..93da2c3f79 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-tr/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tethering\'in internet bağlantısı yok" + "Cihazlar bağlanamıyor" + "Tethering\'i kapat" + "Hotspot veya tethering açık" + "Dolaşım sırasında ek ücretler uygulanabilir" + diff --git a/Tethering/res/values-mcc311-mnc480-uk/strings.xml b/Tethering/res/values-mcc311-mnc480-uk/strings.xml new file mode 100644 index 0000000000..ee0dcd2c4b --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-uk/strings.xml @@ -0,0 +1,24 @@ + + + + + "Телефон, який використовується як модем, не підключений до Інтернету" + "Не вдається підключити пристрої" + "Вимкнути використання телефона як модема" + "Увімкнено точку доступу або використання телефона як модема" + "У роумінгу може стягуватися додаткова плата" + diff --git a/Tethering/res/values-mcc311-mnc480-ur/strings.xml b/Tethering/res/values-mcc311-mnc480-ur/strings.xml new file mode 100644 index 0000000000..41cd28eef9 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-ur/strings.xml @@ -0,0 +1,24 @@ + + + + + "ٹیدرنگ میں انٹرنیٹ نہیں ہے" + "آلات منسلک نہیں ہو سکتے" + "ٹیدرنگ آف کریں" + "ہاٹ اسپاٹ یا ٹیدرنگ آن ہے" + "رومنگ کے دوران اضافی چارجز لاگو ہو سکتے ہیں" + diff --git a/Tethering/res/values-mcc311-mnc480-uz/strings.xml b/Tethering/res/values-mcc311-mnc480-uz/strings.xml new file mode 100644 index 0000000000..c847bc943b --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-uz/strings.xml @@ -0,0 +1,24 @@ + + + + + "Modem internetga ulanmagan" + "Qurilmalar ulanmadi" + "Modem rejimini faolsizlantirish" + "Hotspot yoki modem rejimi yoniq" + "Rouming vaqtida qoʻshimcha haq olinishi mumkin" + diff --git a/Tethering/res/values-mcc311-mnc480-vi/strings.xml b/Tethering/res/values-mcc311-mnc480-vi/strings.xml new file mode 100644 index 0000000000..a74326f09e --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-vi/strings.xml @@ -0,0 +1,24 @@ + + + + + "Không có Internet để chia sẻ kết Internet" + "Các thiết bị không thể kết nối" + "Tắt tính năng chia sẻ Internet" + "Điểm phát sóng hoặc tính năng chia sẻ Internet đang bật" + "Bạn có thể mất thêm phí dữ liệu khi chuyển vùng" + diff --git a/Tethering/res/values-mcc311-mnc480-zh-rCN/strings.xml b/Tethering/res/values-mcc311-mnc480-zh-rCN/strings.xml new file mode 100644 index 0000000000..d7370036e3 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-zh-rCN/strings.xml @@ -0,0 +1,24 @@ + + + + + "共享网络未连接到互联网" + "设备无法连接" + "关闭网络共享" + "热点或网络共享已开启" + "漫游时可能会产生额外的费用" + diff --git a/Tethering/res/values-mcc311-mnc480-zh-rHK/strings.xml b/Tethering/res/values-mcc311-mnc480-zh-rHK/strings.xml new file mode 100644 index 0000000000..f378a9dc2c --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-zh-rHK/strings.xml @@ -0,0 +1,24 @@ + + + + + "無法透過網絡共享連線至互聯網" + "裝置無法連接" + "關閉網絡共享" + "熱點或網絡共享已開啟" + "漫遊時可能需要支付額外費用" + diff --git a/Tethering/res/values-mcc311-mnc480-zh-rTW/strings.xml b/Tethering/res/values-mcc311-mnc480-zh-rTW/strings.xml new file mode 100644 index 0000000000..ea01b943fb --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-zh-rTW/strings.xml @@ -0,0 +1,24 @@ + + + + + "無法透過數據連線連上網際網路" + "裝置無法連線" + "關閉數據連線" + "無線基地台或數據連線已開啟" + "使用漫遊服務可能須支付額外費用" + diff --git a/Tethering/res/values-mcc311-mnc480-zu/strings.xml b/Tethering/res/values-mcc311-mnc480-zu/strings.xml new file mode 100644 index 0000000000..32f6df56f1 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-zu/strings.xml @@ -0,0 +1,24 @@ + + + + + "Ukusebenzisa ifoni njengemodemu akunayo i-inthanethi" + "Amadivayisi awakwazi ukuxhumeka" + "Vala ukusebenzisa ifoni njengemodemu" + "I-hotspot noma ukusebenzisa ifoni njengemodemu kuvuliwe" + "Kungaba nezinkokhelo ezengeziwe uma uzula" + diff --git a/Tethering/res/values-mk/strings.xml b/Tethering/res/values-mk/strings.xml index 0fab8aa476..9ad9b9a589 100644 --- a/Tethering/res/values-mk/strings.xml +++ b/Tethering/res/values-mk/strings.xml @@ -1,8 +1,29 @@ + + - "Поврзувањето или точката на пристап се активни" - "Допрете за поставување." - "Врзувањето е оневозможено" - "Контактирајте со администраторот за детали" + "Активно е врзување или точка на пристап" + "Допрете за поставување." + "Врзувањето е оневозможено" + "Контактирајте со администраторот за детали" + "Статус на точката на пристап и врзувањето" + + + + + diff --git a/Tethering/res/values-ml/strings.xml b/Tethering/res/values-ml/strings.xml index fd7e556e38..9db79ce220 100644 --- a/Tethering/res/values-ml/strings.xml +++ b/Tethering/res/values-ml/strings.xml @@ -1,8 +1,29 @@ + + - "ടെതറിംഗ് അല്ലെങ്കിൽ ഹോട്ട്സ്‌പോട്ട് സജീവമാണ്" - "സജ്ജമാക്കാൻ ടാപ്പുചെയ്യുക." - "ടെതറിംഗ് പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു" - "വിശദവിവരങ്ങൾക്ക് നിങ്ങളുടെ അഡ്മിനെ ബന്ധപ്പെടുക" + "ടെതറിംഗ് അല്ലെങ്കിൽ ഹോട്ട്സ്‌പോട്ട് സജീവമാണ്" + "സജ്ജീകരിക്കാൻ ടാപ്പ് ചെയ്യുക." + "ടെതറിംഗ് പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു" + "വിശദാംശങ്ങൾക്ക് നിങ്ങളുടെ അഡ്മിനെ ബന്ധപ്പെടുക" + "ഹോട്ട്‌സ്പോട്ടിന്റെയും ടെതറിംഗിന്റെയും നില" + + + + + diff --git a/Tethering/res/values-mn/strings.xml b/Tethering/res/values-mn/strings.xml index 4596577c5d..42d1edbace 100644 --- a/Tethering/res/values-mn/strings.xml +++ b/Tethering/res/values-mn/strings.xml @@ -1,8 +1,29 @@ + + - "Модем болгох эсвэл идэвхтэй цэг болгох" - "Тохируулахын тулд товшино уу." - "Модем болгох боломжгүй байна" - "Дэлгэрэнгүй мэдээлэл авахын тулд админтайгаа холбогдоно уу" + "Модем болгох эсвэл сүлжээний цэг идэвхтэй байна" + "Тохируулахын тулд товшино уу." + "Модем болгохыг идэвхгүй болгосон" + "Дэлгэрэнгүй мэдээлэл авахын тулд админтайгаа холбогдоно уу" + "Сүлжээний цэг болон модем болгох төлөв" + + + + + diff --git a/Tethering/res/values-mr/strings.xml b/Tethering/res/values-mr/strings.xml index 85c9ade4fe..13995b6b8a 100644 --- a/Tethering/res/values-mr/strings.xml +++ b/Tethering/res/values-mr/strings.xml @@ -1,8 +1,29 @@ + + - "टेदरिंग किंवा हॉटस्पॉट सक्रिय" - "सेट करण्यासाठी टॅप करा." - "टेदरिंग बंद आहे" - "तपशीलांसाठी तुमच्या प्रशासकाशी संपर्क साधा" + "टेदरिंग किंवा हॉटस्पॉट अ‍ॅक्टिव्ह आहे" + "सेट करण्यासाठी टॅप करा." + "टेदरिंग बंद केले आहे" + "तपशीलांसाठी तुमच्या ॲडमिनशी संपर्क साधा" + "हॉटस्पॉट आणि टेदरिंगची स्थिती" + + + + + diff --git a/Tethering/res/values-ms/strings.xml b/Tethering/res/values-ms/strings.xml index ec6bdbda08..d6a67f37b1 100644 --- a/Tethering/res/values-ms/strings.xml +++ b/Tethering/res/values-ms/strings.xml @@ -1,8 +1,29 @@ + + - "Penambatan atau titik panas aktif" - "Ketik untuk membuat persediaan." - "Penambatan dilumpuhkan" - "Hubungi pentadbir anda untuk maklumat lanjut" + "Penambatan atau tempat liputan aktif" + "Ketik untuk membuat persediaan." + "Penambatan dilumpuhkan" + "Hubungi pentadbir anda untuk mendapatkan maklumat lanjut" + "Status tempat liputan & penambatan" + + + + + diff --git a/Tethering/res/values-my/strings.xml b/Tethering/res/values-my/strings.xml index 83978b67d4..49f6b88a75 100644 --- a/Tethering/res/values-my/strings.xml +++ b/Tethering/res/values-my/strings.xml @@ -1,8 +1,29 @@ + + - "တဆင့်ပြန်လည်လွှင့်ခြင်း သို့မဟုတ် ဟော့စပေါ့ ဖွင့်ထားသည်" - "စနစ်ထည့်သွင်းရန် တို့ပါ။" - "မိုဘိုင်းဖုန်းကို မိုဒမ်အဖြစ်သုံးခြင်းအား ပိတ်ထားသည်" - "အသေးစိတ်အချက်အလက်များအတွက် သင့်စီမံခန့်ခွဲသူကို ဆက်သွယ်ပါ" + "မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်း သို့မဟုတ် ဟော့စပေါ့ ဖွင့်ထားသည်" + "စနစ်ထည့်သွင်းရန် တို့ပါ။" + "မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်းကို ပိတ်ထားသည်" + "အသေးစိတ်အတွက် သင့်စီမံခန့်ခွဲသူကို ဆက်သွယ်ပါ" + "ဟော့စပေါ့နှင့် မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်း အခြေအနေ" + + + + + diff --git a/Tethering/res/values-nb/strings.xml b/Tethering/res/values-nb/strings.xml index 9abf32dd7b..9594e0a70a 100644 --- a/Tethering/res/values-nb/strings.xml +++ b/Tethering/res/values-nb/strings.xml @@ -1,8 +1,29 @@ + + - "Internettdeling eller trådløs sone er aktiv" - "Trykk for å konfigurere." - "Internettdeling er slått av" - "Ta kontakt med administratoren din for å få mer informasjon" + "Internettdeling eller Wi-Fi-sone er aktiv" + "Trykk for å konfigurere." + "Internettdeling er slått av" + "Ta kontakt med administratoren din for å få mer informasjon" + "Status for Wi-Fi-sone og internettdeling" + + + + + diff --git a/Tethering/res/values-ne/strings.xml b/Tethering/res/values-ne/strings.xml index c8869298a5..72ae3a80a9 100644 --- a/Tethering/res/values-ne/strings.xml +++ b/Tethering/res/values-ne/strings.xml @@ -1,8 +1,29 @@ + + - "टेथर गर्ने वा हटस्पट सक्रिय" - "सेटअप गर्न ट्याप गर्नुहोस्।" - "टेदरिङलाई असक्षम पारिएको छ" - "विवरणहरूका लागि आफ्ना प्रशासकलाई सम्पर्क गर्नुहोस्" + "टेदरिङ वा हटस्पट सक्रिय छ" + "सेटअप गर्न ट्याप गर्नुहोस्।" + "टेदरिङ सुविधा असक्षम पारिएको छ" + "विवरणहरूका लागि आफ्ना प्रशासकलाई सम्पर्क गर्नुहोस्" + "हटस्पट तथा टेदरिङको स्थिति" + + + + + diff --git a/Tethering/res/values-nl/strings.xml b/Tethering/res/values-nl/strings.xml index 0ec4bff621..18b2bbfc76 100644 --- a/Tethering/res/values-nl/strings.xml +++ b/Tethering/res/values-nl/strings.xml @@ -1,8 +1,29 @@ + + - "Tethering of hotspot actief" - "Tik om in te stellen." - "Tethering is uitgeschakeld" - "Neem contact op met je beheerder voor meer informatie" + "Tethering of hotspot actief" + "Tik om in te stellen." + "Tethering is uitgeschakeld" + "Neem contact op met je beheerder voor meer informatie" + "Status van hotspot en tethering" + + + + + diff --git a/Tethering/res/values-or/strings.xml b/Tethering/res/values-or/strings.xml index 457685795a..a15a6db42a 100644 --- a/Tethering/res/values-or/strings.xml +++ b/Tethering/res/values-or/strings.xml @@ -1,8 +1,29 @@ + + - "ଟିଥରିଙ୍ଗ କିମ୍ୱା ହଟସ୍ପଟ୍‌ ସକ୍ରିୟ ଅଛି" - "ସେଟଅପ୍‍ କରିବାକୁ ଟାପ୍‍ କରନ୍ତୁ।" - "ଟିଥରିଙ୍ଗ ଅକ୍ଷମ କରାଯାଇଛି" - "ବିବରଣୀ ପାଇଁ ନିଜ ଆଡମିନ୍‌ଙ୍କ ସହ ଯୋଗାଯୋଗ କରନ୍ତୁ" + "ଟିଥେରିଂ କିମ୍ୱା ହଟସ୍ପଟ୍ ସକ୍ରିୟ ଅଛି" + "ସେଟ୍ ଅପ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ।" + "ଟିଥେରିଂ ଅକ୍ଷମ କରାଯାଇଛି" + "ବିବରଣୀଗୁଡ଼ିକ ପାଇଁ ଆପଣଙ୍କ ଆଡମିନଙ୍କ ସହ ଯୋଗାଯୋଗ କରନ୍ତୁ" + "ହଟସ୍ପଟ୍ ଓ ଟିଥେରିଂ ସ୍ଥିତି" + + + + + diff --git a/Tethering/res/values-pa/strings.xml b/Tethering/res/values-pa/strings.xml index deddf2ea27..a8235e423e 100644 --- a/Tethering/res/values-pa/strings.xml +++ b/Tethering/res/values-pa/strings.xml @@ -1,8 +1,29 @@ + + - "ਟੈਦਰਿੰਗ ਜਾਂ ਹੌਟਸਪੌਟ ਕਿਰਿਆਸ਼ੀਲ" - "ਸਥਾਪਤ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।" - "ਟੈਦਰਿੰਗ ਨੂੰ ਅਯੋਗ ਬਣਾਇਆ ਗਿਆ ਹੈ" - "ਵੇਰਵਿਆਂ ਲਈ ਆਪਣੇ ਪ੍ਰਸ਼ਾਸਕ ਨੂੰ ਸੰਪਰਕ ਕਰੋ" + "ਟੈਦਰਿੰਗ ਜਾਂ ਹੌਟਸਪੌਟ ਕਿਰਿਆਸ਼ੀਲ" + "ਸੈੱਟਅੱਪ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।" + "ਟੈਦਰਿੰਗ ਨੂੰ ਬੰਦ ਕੀਤਾ ਗਿਆ ਹੈ" + "ਵੇਰਵਿਆਂ ਲਈ ਆਪਣੇ ਪ੍ਰਸ਼ਾਸਕ ਨਾਲ ਸੰਪਰਕ ਕਰੋ" + "ਹੌਟਸਪੌਟ ਅਤੇ ਟੈਦਰਿੰਗ ਦੀ ਸਥਿਤੀ" + + + + + diff --git a/Tethering/res/values-pl/strings.xml b/Tethering/res/values-pl/strings.xml index 48d8468935..ccb017d43f 100644 --- a/Tethering/res/values-pl/strings.xml +++ b/Tethering/res/values-pl/strings.xml @@ -1,8 +1,29 @@ + + - "Aktywny tethering lub punkt dostępu" - "Kliknij, by skonfigurować." - "Tethering został wyłączony" - "Aby uzyskać szczegółowe informacje, skontaktuj się z administratorem" + "Aktywny tethering lub punkt dostępu" + "Kliknij, by skonfigurować" + "Tethering został wyłączony" + "Aby uzyskać szczegółowe informacje, skontaktuj się z administratorem" + "Hotspot i tethering – stan" + + + + + diff --git a/Tethering/res/values-pt-rBR/strings.xml b/Tethering/res/values-pt-rBR/strings.xml index 32c22b8713..a0a4745f93 100644 --- a/Tethering/res/values-pt-rBR/strings.xml +++ b/Tethering/res/values-pt-rBR/strings.xml @@ -1,8 +1,29 @@ + + - "Ponto de acesso ou tethering ativo" - "Toque para configurar." - "Tethering desativado" - "Fale com seu administrador para saber detalhes" + "Ponto de acesso ou tethering ativo" + "Toque para configurar." + "Tethering desativado" + "Fale com seu administrador para saber detalhes" + "Status de ponto de acesso e tethering" + + + + + diff --git a/Tethering/res/values-pt-rPT/strings.xml b/Tethering/res/values-pt-rPT/strings.xml index 641e22f44f..e3f03fcc69 100644 --- a/Tethering/res/values-pt-rPT/strings.xml +++ b/Tethering/res/values-pt-rPT/strings.xml @@ -1,8 +1,29 @@ + + - "Ligação ponto a ponto ou hotspot activos" - "Toque para configurar." - "A ligação (à Internet) via telemóvel está desativada." - "Contacte o gestor para obter detalhes." + "Ligação (à Internet) via telemóvel ou zona Wi-Fi ativas" + "Toque para configurar." + "A ligação (à Internet) via telemóvel está desativada." + "Contacte o administrador para obter detalhes." + "Estado da zona Wi-Fi e da ligação (à Internet) via telemóvel" + + + + + diff --git a/Tethering/res/values-pt/strings.xml b/Tethering/res/values-pt/strings.xml index 32c22b8713..a0a4745f93 100644 --- a/Tethering/res/values-pt/strings.xml +++ b/Tethering/res/values-pt/strings.xml @@ -1,8 +1,29 @@ + + - "Ponto de acesso ou tethering ativo" - "Toque para configurar." - "Tethering desativado" - "Fale com seu administrador para saber detalhes" + "Ponto de acesso ou tethering ativo" + "Toque para configurar." + "Tethering desativado" + "Fale com seu administrador para saber detalhes" + "Status de ponto de acesso e tethering" + + + + + diff --git a/Tethering/res/values-ro/strings.xml b/Tethering/res/values-ro/strings.xml index f861f733b4..5706a4a69c 100644 --- a/Tethering/res/values-ro/strings.xml +++ b/Tethering/res/values-ro/strings.xml @@ -1,8 +1,29 @@ + + - "Tethering sau hotspot activ" - "Atingeți ca să configurați." - "Tetheringul este dezactivat" - "Contactați administratorul pentru detalii" + "Tethering sau hotspot activ" + "Atingeți ca să configurați." + "Tetheringul este dezactivat" + "Contactați administratorul pentru detalii" + "Starea hotspotului și a tetheringului" + + + + + diff --git a/Tethering/res/values-ru/strings.xml b/Tethering/res/values-ru/strings.xml index 027cb410c5..7cb6f7db3f 100644 --- a/Tethering/res/values-ru/strings.xml +++ b/Tethering/res/values-ru/strings.xml @@ -1,8 +1,29 @@ + + - "Включен режим модема" - "Нажмите, чтобы настроить." - "Включить режим модема нельзя" - "Обратитесь к администратору, чтобы узнать подробности." + "Включен режим модема или точка доступа" + "Нажмите, чтобы настроить." + "Использование телефона в качестве модема запрещено" + "Чтобы узнать подробности, обратитесь к администратору." + "Статус хот-спота и режима модема" + + + + + diff --git a/Tethering/res/values-si/strings.xml b/Tethering/res/values-si/strings.xml index 7d8599f2c2..ec34c22de7 100644 --- a/Tethering/res/values-si/strings.xml +++ b/Tethering/res/values-si/strings.xml @@ -1,8 +1,29 @@ + + - "ටෙදරින් හෝ හොට්ස්පොට් සක්‍රීයයි" - "පිහිටුවීමට තට්ටු කරන්න." - "ටෙදරින් අබල කර ඇත" - "විස්තර සඳහා ඔබගේ පරිපාලක අමතන්න" + "ටෙදරින් හෝ හොට්ස්පොට් සක්‍රීයයි" + "පිහිටුවීමට තට්ටු කරන්න." + "ටෙදරින් අබල කර ඇත" + "විස්තර සඳහා ඔබගේ පරිපාලක අමතන්න" + "හොට්ස්පොට් & ටෙදරින් තත්ත්වය" + + + + + diff --git a/Tethering/res/values-sk/strings.xml b/Tethering/res/values-sk/strings.xml index a8fe297c00..43e787c84f 100644 --- a/Tethering/res/values-sk/strings.xml +++ b/Tethering/res/values-sk/strings.xml @@ -1,8 +1,29 @@ + + - "Tethering alebo prístupový bod je aktívny" - "Klepnutím prejdete na nastavenie." - "Tethering je deaktivovaný" - "O podrobnosti požiadajte svojho správcu" + "Tethering alebo prístupový bod je aktívny" + "Klepnutím prejdete na nastavenie." + "Tethering je deaktivovaný" + "O podrobnosti požiadajte svojho správcu" + "Stav hotspotu a tetheringu" + + + + + diff --git a/Tethering/res/values-sl/strings.xml b/Tethering/res/values-sl/strings.xml index b5e5e3856f..59433626a1 100644 --- a/Tethering/res/values-sl/strings.xml +++ b/Tethering/res/values-sl/strings.xml @@ -1,8 +1,29 @@ + + - "Aktivna povezava z internetom ali dostopna točka sta aktivni" - "Dotaknite se, če želite nastaviti." - "Povezava z internetom prek mobilnega telefona je onemogočena" - "Za podrobnosti se obrnite na skrbnika" + "Povezava z internetom prek mobilnega telefona ali dostopna točka je aktivna" + "Dotaknite se, če želite nastaviti." + "Povezava z internetom prek mobilnega telefona je onemogočena" + "Za podrobnosti se obrnite na skrbnika" + "Stanje dostopne točke in povezave z internetom prek mobilnega telefona" + + + + + diff --git a/Tethering/res/values-sq/strings.xml b/Tethering/res/values-sq/strings.xml index fdd4906cc5..21e11558bb 100644 --- a/Tethering/res/values-sq/strings.xml +++ b/Tethering/res/values-sq/strings.xml @@ -1,8 +1,29 @@ + + - "Lidhja e çiftimit ose ajo e qasjes në zona publike interneti është aktive" - "Trokit për ta konfiguruar." - "Lidhja e çiftimit është çaktivizuar" - "Kontakto me administratorin për detaje" + "Ndarja e internetit ose zona e qasjes së internetit është aktive" + "Trokit për ta konfiguruar." + "Ndarja e internetit është çaktivizuar" + "Kontakto me administratorin për detaje" + "Statusi i zonës së qasjes dhe ndarjes së internetit" + + + + + diff --git a/Tethering/res/values-sr/strings.xml b/Tethering/res/values-sr/strings.xml index 9fab345897..e2e4dc6361 100644 --- a/Tethering/res/values-sr/strings.xml +++ b/Tethering/res/values-sr/strings.xml @@ -1,8 +1,29 @@ + + - "Активно повезивање са интернетом преко мобилног уређаја или хотспот" - "Додирните да бисте подесили." - "Привезивање је онемогућено" - "Потражите детаље од администратора" + "Привезивање или хотспот је активан" + "Додирните да бисте подесили." + "Привезивање је онемогућено" + "Потражите детаље од администратора" + "Статус хотспота и привезивања" + + + + + diff --git a/Tethering/res/values-sv/strings.xml b/Tethering/res/values-sv/strings.xml index 10eeb0fe12..72702c2858 100644 --- a/Tethering/res/values-sv/strings.xml +++ b/Tethering/res/values-sv/strings.xml @@ -1,8 +1,29 @@ + + - "Internetdelning eller surfzon aktiverad" - "Tryck om du vill konfigurera." - "Internetdelning har inaktiverats" - "Kontakta administratören om du vill veta mer" + "Internetdelning eller surfzon har aktiverats" + "Tryck om du vill konfigurera." + "Internetdelning har inaktiverats" + "Kontakta administratören om du vill veta mer" + "Trådlös surfzon och internetdelning har inaktiverats" + + + + + diff --git a/Tethering/res/values-sw/strings.xml b/Tethering/res/values-sw/strings.xml index 3353963077..65e4aa8ceb 100644 --- a/Tethering/res/values-sw/strings.xml +++ b/Tethering/res/values-sw/strings.xml @@ -1,8 +1,29 @@ + + - "Kushiriki au kusambaza intaneti kumewashwa" - "Gusa ili uweke mipangilio." - "Umezima kipengele cha kusambaza mtandao" - "Wasiliana na msimamizi wako ili upate maelezo zaidi" + "Kusambaza mtandao au mtandaopepe umewashwa" + "Gusa ili uweke mipangilio." + "Umezima kipengele cha kusambaza mtandao" + "Wasiliana na msimamizi wako ili upate maelezo zaidi" + "Mtandaopepe na hali ya kusambaza mtandao" + + + + + diff --git a/Tethering/res/values-ta/strings.xml b/Tethering/res/values-ta/strings.xml index b1e5cc2413..4aba62d4ab 100644 --- a/Tethering/res/values-ta/strings.xml +++ b/Tethering/res/values-ta/strings.xml @@ -1,8 +1,29 @@ + + - "டெதெரிங்/ஹாட்ஸ்பாட் இயங்குகிறது" - "அமைக்க, தட்டவும்." - "இணைப்பு முறை முடக்கப்பட்டுள்ளது" - "விவரங்களுக்கு, உங்கள் நிர்வாகியைத் தொடர்புகொள்ளவும்" + "டெதெரிங் அல்லது ஹாட்ஸ்பாட் இயங்குகிறது" + "அமைக்க, தட்டவும்." + "டெதெரிங் முடக்கப்பட்டுள்ளது" + "விவரங்களுக்கு உங்கள் நிர்வாகியைத் தொடர்புகொள்ளவும்" + "ஹாட்ஸ்பாட் & டெதெரிங் நிலை" + + + + + diff --git a/Tethering/res/values-te/strings.xml b/Tethering/res/values-te/strings.xml index aae40dee40..1f91791341 100644 --- a/Tethering/res/values-te/strings.xml +++ b/Tethering/res/values-te/strings.xml @@ -1,8 +1,29 @@ + + - "టీథర్ చేయబడినది లేదా హాట్‌స్పాట్ సక్రియంగా ఉండేది" - "సెటప్ చేయడానికి నొక్కండి." - "టెథెరింగ్ నిలిపివేయబడింది" - "వివరాల కోసం మీ నిర్వాహకులను సంప్రదించండి" + "టెథరింగ్ లేదా హాట్‌స్పాట్ యాక్టివ్‌గా ఉంది" + "సెటప్ చేయడానికి ట్యాప్ చేయండి." + "టెథరింగ్ డిజేబుల్ చేయబడింది" + "వివరాల కోసం మీ అడ్మిన్‌ని సంప్రదించండి" + "హాట్‌స్పాట్ & టెథరింగ్ స్థితి" + + + + + diff --git a/Tethering/res/values-th/strings.xml b/Tethering/res/values-th/strings.xml index 1b800565ad..44171c0db8 100644 --- a/Tethering/res/values-th/strings.xml +++ b/Tethering/res/values-th/strings.xml @@ -1,8 +1,29 @@ + + - "การปล่อยสัญญาณหรือฮอตสปอตทำงานอยู่" - "แตะเพื่อตั้งค่า" - "ปิดใช้การเชื่อมต่ออินเทอร์เน็ตผ่านมือถือแล้ว" - "ติดต่อผู้ดูแลระบบเพื่อขอรายละเอียด" + "การเชื่อมต่ออินเทอร์เน็ตผ่านมือถือหรือฮอตสปอตทำงานอยู่" + "แตะเพื่อตั้งค่า" + "ปิดใช้การเชื่อมต่ออินเทอร์เน็ตผ่านมือถือแล้ว" + "ติดต่อผู้ดูแลระบบเพื่อขอรายละเอียด" + "สถานะฮอตสปอตและการเชื่อมต่ออินเทอร์เน็ตผ่านมือถือ" + + + + + diff --git a/Tethering/res/values-tl/strings.xml b/Tethering/res/values-tl/strings.xml index 12863f90e1..7347dd3e62 100644 --- a/Tethering/res/values-tl/strings.xml +++ b/Tethering/res/values-tl/strings.xml @@ -1,8 +1,29 @@ + + - "Pagsasama o aktibong hotspot" - "I-tap upang i-set up." - "Naka-disable ang pag-tether" - "Makipag-ugnayan sa iyong admin para sa mga detalye" + "Aktibo ang pag-tether o hotspot" + "I-tap para i-set up." + "Naka-disable ang pag-tether" + "Makipag-ugnayan sa iyong admin para sa mga detalye" + "Status ng hotspot at pag-tether" + + + + + diff --git a/Tethering/res/values-tr/strings.xml b/Tethering/res/values-tr/strings.xml index bfcf1ace2c..32030f1765 100644 --- a/Tethering/res/values-tr/strings.xml +++ b/Tethering/res/values-tr/strings.xml @@ -1,8 +1,29 @@ + + - "Tethering veya hotspot etkin" - "Ayarlamak için dokunun." - "Tethering devre dışı bırakıldı" - "Ayrıntılı bilgi için yöneticinize başvurun" + "Tethering veya hotspot etkin" + "Ayarlamak için dokunun." + "Tethering devre dışı bırakıldı" + "Ayrıntılı bilgi için yöneticinize başvurun" + "Hotspot ve tethering durumu" + + + + + diff --git a/Tethering/res/values-uk/strings.xml b/Tethering/res/values-uk/strings.xml index 8e159c0723..1ca89b3f78 100644 --- a/Tethering/res/values-uk/strings.xml +++ b/Tethering/res/values-uk/strings.xml @@ -1,8 +1,29 @@ + + - "Прив\'язка чи точка дост. активна" - "Торкніться, щоб налаштувати." - "Використання телефона в режимі модема вимкнено" - "Щоб дізнатися більше, зв’яжіться з адміністратором" + "Модем чи точка доступу активні" + "Натисніть, щоб налаштувати." + "Використання телефона як модема вимкнено" + "Щоб дізнатися більше, зв\'яжіться з адміністратором" + "Статус точки доступу та модема" + + + + + diff --git a/Tethering/res/values-ur/strings.xml b/Tethering/res/values-ur/strings.xml index 89195d4aae..d72c7d4195 100644 --- a/Tethering/res/values-ur/strings.xml +++ b/Tethering/res/values-ur/strings.xml @@ -1,8 +1,29 @@ + + - "ٹیدرنگ یا ہاٹ اسپاٹ فعال" - "سیٹ اپ کرنے کیلئے تھپتھپائیں۔" - "ٹیدرنگ غیر فعال ہے" - "تفصیلات کے لئے اپنے منتظم سے رابطہ کریں" + "ٹیدرنگ یا ہاٹ اسپاٹ فعال" + "سیٹ اپ کرنے کیلئے تھپتھپائیں۔" + "ٹیدرنگ غیر فعال ہے" + "تفصیلات کے لئے اپنے منتظم سے رابطہ کریں" + "ہاٹ اسپاٹ اور ٹیتھرنگ کا اسٹیٹس" + + + + + diff --git a/Tethering/res/values-uz/strings.xml b/Tethering/res/values-uz/strings.xml index 0ac4d4a741..af3b2ebb35 100644 --- a/Tethering/res/values-uz/strings.xml +++ b/Tethering/res/values-uz/strings.xml @@ -1,8 +1,29 @@ + + - "Modem rejimi yoniq" - "Sozlash uchun bosing." - "Modem rejimi faolsizlantirildi" - "Tafsilotlari uchun administratoringizga murojaat qiling" + "Modem rejimi yoki hotspot yoniq" + "Sozlash uchun bosing." + "Modem rejimi faolsizlantirildi" + "Tafsilotlari uchun administratoringizga murojaat qiling" + "Hotspot va modem rejimi holati" + + + + + diff --git a/Tethering/res/values-vi/strings.xml b/Tethering/res/values-vi/strings.xml index 85a4db8aa5..21a0735922 100644 --- a/Tethering/res/values-vi/strings.xml +++ b/Tethering/res/values-vi/strings.xml @@ -1,8 +1,29 @@ + + - "Chức năng điểm truy cập Internet hoặc điểm phát sóng đang hoạt động" - "Nhấn để thiết lập." - "Đã tắt tính năng chia sẻ kết nối" - "Hãy liên hệ với quản trị viên của bạn để biết chi tiết" + "Tính năng chia sẻ Internet hoặc điểm phát sóng đang hoạt động" + "Hãy nhấn để thiết lập." + "Đã tắt tính năng chia sẻ Internet" + "Hãy liên hệ với quản trị viên của bạn để biết chi tiết" + "Trạng thái điểm phát sóng và chia sẻ Internet" + + + + + diff --git a/Tethering/res/values-zh-rCN/strings.xml b/Tethering/res/values-zh-rCN/strings.xml index ff1fe03953..98e3b4b46f 100644 --- a/Tethering/res/values-zh-rCN/strings.xml +++ b/Tethering/res/values-zh-rCN/strings.xml @@ -1,8 +1,29 @@ + + - "网络共享或热点已启用" - "点按即可进行设置。" - "网络共享已停用" - "请与您的管理员联系以了解详情" + "网络共享或热点已启用" + "点按即可设置。" + "网络共享已停用" + "如需了解详情,请与您的管理员联系" + "热点和网络共享状态" + + + + + diff --git a/Tethering/res/values-zh-rHK/strings.xml b/Tethering/res/values-zh-rHK/strings.xml index 0de39fac97..9cafd42dd4 100644 --- a/Tethering/res/values-zh-rHK/strings.xml +++ b/Tethering/res/values-zh-rHK/strings.xml @@ -1,8 +1,29 @@ + + - "已啟用網絡共享或熱點" - "輕按即可設定。" - "網絡共享已停用" - "請聯絡您的管理員以瞭解詳情" + "網絡共享或熱點已啟用" + "輕按即可設定。" + "網絡共享已停用" + "請聯絡您的管理員以瞭解詳情" + "熱點和網絡共享狀態" + + + + + diff --git a/Tethering/res/values-zh-rTW/strings.xml b/Tethering/res/values-zh-rTW/strings.xml index 9a117bbca4..9d738a76eb 100644 --- a/Tethering/res/values-zh-rTW/strings.xml +++ b/Tethering/res/values-zh-rTW/strings.xml @@ -1,8 +1,29 @@ + + - "網路共用或無線基地台已啟用" - "輕觸即可進行設定。" - "數據連線已停用" - "詳情請洽你的管理員" + "數據連線或無線基地台已啟用" + "輕觸即可進行設定。" + "數據連線已停用" + "詳情請洽你的管理員" + "無線基地台與數據連線狀態" + + + + + diff --git a/Tethering/res/values-zu/strings.xml b/Tethering/res/values-zu/strings.xml index 8fe10d86cb..f210f8726e 100644 --- a/Tethering/res/values-zu/strings.xml +++ b/Tethering/res/values-zu/strings.xml @@ -1,8 +1,29 @@ + + - "Ukusebenzisa njengemodemu noma i-hotspot ephathekayo kuvuliwe" - "Thepha ukuze usethe." - "Ukusebenzisa ifoni njengemodemu kukhutshaziwe" - "Xhumana nomphathi wakho ukuze uthole imininingwane" + "Ukusebenzisa njengemodemu noma i-hotspot ephathekayo kuvuliwe" + "Thepha ukuze usethe." + "Ukusebenzisa ifoni njengemodemu kukhutshaziwe" + "Xhumana nomphathi wakho ukuze uthole imininingwane" + "I-Hotspot nesimo sokusebenzisa ifoni njengemodemu" + + + + + From 86152672a6ca9f581d860063029175811e7cfa2c Mon Sep 17 00:00:00 2001 From: Paul Hu Date: Wed, 22 Apr 2020 16:50:48 +0000 Subject: [PATCH 111/188] Add TetheredClient tests Test APIs below: getAddresses() getMacAddress() getTetheringType() AddressInfo.getAddress() AddressInfo.getHostname() AddressInfo.writeToParcel(android.os.Parcel, int) Bug: 153614365 Bug: 153613717 Test: atest TetheringTests Change-Id: Ic7cbebe54a38af5b5c4639eb7641a20de6864a26 Merged-In: Ic7cbebe54a38af5b5c4639eb7641a20de6864a26 (cherry picked from commit fa882fdb968c8467924b626e6392930b028427e7, aosp/1292438) --- .../common/android/net/TetheredClientTest.kt | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/Tethering/tests/unit/common/android/net/TetheredClientTest.kt b/Tethering/tests/unit/common/android/net/TetheredClientTest.kt index a20a0dfd9c..55c59dd08f 100644 --- a/Tethering/tests/unit/common/android/net/TetheredClientTest.kt +++ b/Tethering/tests/unit/common/android/net/TetheredClientTest.kt @@ -33,7 +33,9 @@ private val TEST_MACADDR = MacAddress.fromBytes(byteArrayOf(12, 23, 34, 45, 56, private val TEST_OTHER_MACADDR = MacAddress.fromBytes(byteArrayOf(23, 34, 45, 56, 67, 78)) private val TEST_ADDR1 = makeLinkAddress("192.168.113.3", prefixLength = 24, expTime = 123L) private val TEST_ADDR2 = makeLinkAddress("fe80::1:2:3", prefixLength = 64, expTime = 456L) -private val TEST_ADDRINFO1 = AddressInfo(TEST_ADDR1, "test_hostname") +private val TEST_HOSTNAME = "test_hostname" +private val TEST_OTHER_HOSTNAME = "test_other_hostname" +private val TEST_ADDRINFO1 = AddressInfo(TEST_ADDR1, TEST_HOSTNAME) private val TEST_ADDRINFO2 = AddressInfo(TEST_ADDR2, null) private fun makeLinkAddress(addr: String, prefixLength: Int, expTime: Long) = LinkAddress( @@ -49,6 +51,7 @@ private fun makeLinkAddress(addr: String, prefixLength: Int, expTime: Long) = Li class TetheredClientTest { @Test fun testParceling() { + assertParcelSane(TEST_ADDRINFO1, fieldCount = 2) assertParcelSane(makeTestClient(), fieldCount = 3) } @@ -65,7 +68,7 @@ class TetheredClientTest { // Different hostname assertNotEquals(makeTestClient(), TetheredClient( TEST_MACADDR, - listOf(AddressInfo(TEST_ADDR1, "test_other_hostname"), TEST_ADDRINFO2), + listOf(AddressInfo(TEST_ADDR1, TEST_OTHER_HOSTNAME), TEST_ADDRINFO2), TETHERING_BLUETOOTH)) // Null hostname @@ -97,6 +100,21 @@ class TetheredClientTest { TETHERING_USB), client1.addAddresses(client2)) } + @Test + fun testGetters() { + assertEquals(TEST_MACADDR, makeTestClient().macAddress) + assertEquals(listOf(TEST_ADDRINFO1, TEST_ADDRINFO2), makeTestClient().addresses) + assertEquals(TETHERING_BLUETOOTH, makeTestClient().tetheringType) + } + + @Test + fun testAddressInfo_Getters() { + assertEquals(TEST_ADDR1, TEST_ADDRINFO1.address) + assertEquals(TEST_ADDR2, TEST_ADDRINFO2.address) + assertEquals(TEST_HOSTNAME, TEST_ADDRINFO1.hostname) + assertEquals(null, TEST_ADDRINFO2.hostname) + } + private fun makeTestClient() = TetheredClient( TEST_MACADDR, listOf(TEST_ADDRINFO1, TEST_ADDRINFO2), From 0439c2d2047aad6fb4e740e28c4cf62ced5baf35 Mon Sep 17 00:00:00 2001 From: Paul Hu Date: Thu, 23 Apr 2020 09:07:00 +0000 Subject: [PATCH 112/188] [TNU09] Adjust restricted notification 1. Let restricted notification that can be dismissed. 2. Only put up restricted notification when any of tethering is activating. Bug: 154214549 Test: atest TetheringTests Change-Id: Ib980aca154036828abdab35e3bb11d42f85ff610 Merged-In: Ib980aca154036828abdab35e3bb11d42f85ff610 (cherry picked from commit 2eb66bdbe48c9a97e55e590ad767c4b598f9a08c, aosp/1290334) --- .../networkstack/tethering/Tethering.java | 18 ++++--- .../TetheringNotificationUpdater.java | 12 ++--- .../networkstack/tethering/TetheringTest.java | 50 +++++++++---------- 3 files changed, 43 insertions(+), 37 deletions(-) diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java index 05cf68efd7..4e16c49caa 100644 --- a/Tethering/src/com/android/networkstack/tethering/Tethering.java +++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java @@ -1006,6 +1006,11 @@ public class Tethering { } } + @VisibleForTesting + boolean isTetheringActive() { + return mActiveTetheringRequests.size() > 0; + } + @VisibleForTesting protected static class UserRestrictionActionListener { private final UserManager mUserManager; @@ -1043,13 +1048,14 @@ public class Tethering { return; } - // Restricted notification is shown when tethering function is disallowed on - // user's device. - mNotificationUpdater.notifyTetheringDisabledByRestriction(); - - // Untether from all downstreams since tethering is disallowed. - mWrapper.untetherAll(); + if (mWrapper.isTetheringActive()) { + // Restricted notification is shown when tethering function is disallowed on + // user's device. + mNotificationUpdater.notifyTetheringDisabledByRestriction(); + // Untether from all downstreams since tethering is disallowed. + mWrapper.untetherAll(); + } // TODO(b/148139325): send tetheringSupported on restriction change } } diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java b/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java index 7fd6b61ccd..d03deda37f 100644 --- a/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java @@ -267,7 +267,7 @@ public class TetheringNotificationUpdater { null /* options */); showNotification(R.drawable.stat_sys_tether_general, title, message, - RESTRICTED_NOTIFICATION_ID, pi, new Action[0]); + RESTRICTED_NOTIFICATION_ID, false /* ongoing */, pi, new Action[0]); } private void notifyTetheringNoUpstream() { @@ -288,7 +288,7 @@ public class TetheringNotificationUpdater { final Action action = new Action.Builder(NO_ICON_ID, disableButton, pi).build(); showNotification(R.drawable.stat_sys_tether_general, title, message, - NO_UPSTREAM_NOTIFICATION_ID, null /* pendingIntent */, action); + NO_UPSTREAM_NOTIFICATION_ID, true /* ongoing */, null /* pendingIntent */, action); } private boolean setupRoamingNotification() { @@ -310,7 +310,7 @@ public class TetheringNotificationUpdater { null /* options */); showNotification(R.drawable.stat_sys_tether_general, title, message, - ROAMING_NOTIFICATION_ID, pi, new Action[0]); + ROAMING_NOTIFICATION_ID, true /* ongoing */, pi, new Action[0]); return NOTIFY_DONE; } @@ -327,14 +327,14 @@ public class TetheringNotificationUpdater { } private void showNotification(@DrawableRes final int iconId, @NonNull final String title, - @NonNull final String message, @NotificationId final int id, @Nullable PendingIntent pi, - @NonNull final Action... actions) { + @NonNull final String message, @NotificationId final int id, final boolean ongoing, + @Nullable PendingIntent pi, @NonNull final Action... actions) { final Notification notification = new Notification.Builder(mContext, mChannel.getId()) .setSmallIcon(iconId) .setContentTitle(title) .setContentText(message) - .setOngoing(true) + .setOngoing(ongoing) .setColor(mContext.getColor( android.R.color.system_notification_accent_color)) .setVisibility(Notification.VISIBILITY_PUBLIC) 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 cf0548304a..0c86eeb6cd 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java @@ -1079,12 +1079,12 @@ public class TetheringTest { } private void runUserRestrictionsChange( - boolean currentDisallow, boolean nextDisallow, String[] activeTetheringIfacesList, + boolean currentDisallow, boolean nextDisallow, boolean isTetheringActive, int expectedInteractionsWithShowNotification) throws Exception { final Bundle newRestrictions = new Bundle(); newRestrictions.putBoolean(UserManager.DISALLOW_CONFIG_TETHERING, nextDisallow); final Tethering mockTethering = mock(Tethering.class); - when(mockTethering.getTetheredIfaces()).thenReturn(activeTetheringIfacesList); + when(mockTethering.isTetheringActive()).thenReturn(isTetheringActive); when(mUserManager.getUserRestrictions()).thenReturn(newRestrictions); final Tethering.UserRestrictionActionListener ural = @@ -1100,63 +1100,63 @@ public class TetheringTest { } @Test - public void testDisallowTetheringWhenNoTetheringInterfaceIsActive() throws Exception { - final String[] emptyActiveIfacesList = new String[]{}; + public void testDisallowTetheringWhenTetheringIsNotActive() throws Exception { + final boolean isTetheringActive = false; + final boolean currDisallow = false; + final boolean nextDisallow = true; + final int expectedInteractionsWithShowNotification = 0; + + runUserRestrictionsChange(currDisallow, nextDisallow, isTetheringActive, + expectedInteractionsWithShowNotification); + } + + @Test + public void testDisallowTetheringWhenTetheringIsActive() throws Exception { + final boolean isTetheringActive = true; final boolean currDisallow = false; final boolean nextDisallow = true; final int expectedInteractionsWithShowNotification = 1; - runUserRestrictionsChange(currDisallow, nextDisallow, emptyActiveIfacesList, + runUserRestrictionsChange(currDisallow, nextDisallow, isTetheringActive, expectedInteractionsWithShowNotification); } @Test - public void testDisallowTetheringWhenAtLeastOneTetheringInterfaceIsActive() throws Exception { - final String[] nonEmptyActiveIfacesList = new String[]{TEST_WLAN_IFNAME}; - final boolean currDisallow = false; - final boolean nextDisallow = true; - final int expectedInteractionsWithShowNotification = 1; - - runUserRestrictionsChange(currDisallow, nextDisallow, nonEmptyActiveIfacesList, - expectedInteractionsWithShowNotification); - } - - @Test - public void testAllowTetheringWhenNoTetheringInterfaceIsActive() throws Exception { - final String[] nonEmptyActiveIfacesList = new String[]{}; + public void testAllowTetheringWhenTetheringIsNotActive() throws Exception { + final boolean isTetheringActive = false; final boolean currDisallow = true; final boolean nextDisallow = false; final int expectedInteractionsWithShowNotification = 0; - runUserRestrictionsChange(currDisallow, nextDisallow, nonEmptyActiveIfacesList, + runUserRestrictionsChange(currDisallow, nextDisallow, isTetheringActive, expectedInteractionsWithShowNotification); } @Test - public void testAllowTetheringWhenAtLeastOneTetheringInterfaceIsActive() throws Exception { - final String[] nonEmptyActiveIfacesList = new String[]{TEST_WLAN_IFNAME}; + public void testAllowTetheringWhenTetheringIsActive() throws Exception { + final boolean isTetheringActive = true; final boolean currDisallow = true; final boolean nextDisallow = false; final int expectedInteractionsWithShowNotification = 0; - runUserRestrictionsChange(currDisallow, nextDisallow, nonEmptyActiveIfacesList, + runUserRestrictionsChange(currDisallow, nextDisallow, isTetheringActive, expectedInteractionsWithShowNotification); } @Test public void testDisallowTetheringUnchanged() throws Exception { - final String[] nonEmptyActiveIfacesList = new String[]{TEST_WLAN_IFNAME}; + final boolean isTetheringActive = true; final int expectedInteractionsWithShowNotification = 0; boolean currDisallow = true; boolean nextDisallow = true; - runUserRestrictionsChange(currDisallow, nextDisallow, nonEmptyActiveIfacesList, + runUserRestrictionsChange(currDisallow, nextDisallow, isTetheringActive, expectedInteractionsWithShowNotification); currDisallow = false; nextDisallow = false; - runUserRestrictionsChange(currDisallow, nextDisallow, nonEmptyActiveIfacesList, + runUserRestrictionsChange(currDisallow, nextDisallow, isTetheringActive, expectedInteractionsWithShowNotification); } From 8a35765b6fe1de3a86d995895cbca24c68b69182 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Mon, 27 Apr 2020 00:04:34 -0700 Subject: [PATCH 113/188] Import translations. DO NOT MERGE Auto-generated-cl: translation import Change-Id: I08496ebb9bad7c95a6e9df43ef57c55a9956c9d8 --- Tethering/res/values-mcc310-mnc004-ne/strings.xml | 12 ++++-------- Tethering/res/values-mcc310-mnc004-ta/strings.xml | 12 ++++-------- Tethering/res/values-mcc311-mnc480-ne/strings.xml | 12 ++++-------- Tethering/res/values-mcc311-mnc480-ta/strings.xml | 12 ++++-------- 4 files changed, 16 insertions(+), 32 deletions(-) diff --git a/Tethering/res/values-mcc310-mnc004-ne/strings.xml b/Tethering/res/values-mcc310-mnc004-ne/strings.xml index 2a7330098f..12d6c2cfba 100644 --- a/Tethering/res/values-mcc310-mnc004-ne/strings.xml +++ b/Tethering/res/values-mcc310-mnc004-ne/strings.xml @@ -16,13 +16,9 @@ - - - - - - - - + "Tethering has no internet" + "यन्त्रहरू कनेक्ट गर्न सकिएन" + "टेदरिङ निष्क्रिय पार्नुहोस्" + "हटस्पट वा टेदरिङ सक्रिय छ" "रोमिङ सेवा प्रयोग गर्दा अतिरिक्त शुल्क लाग्न सक्छ" diff --git a/Tethering/res/values-mcc310-mnc004-ta/strings.xml b/Tethering/res/values-mcc310-mnc004-ta/strings.xml index ea04821e33..f4b15aab19 100644 --- a/Tethering/res/values-mcc310-mnc004-ta/strings.xml +++ b/Tethering/res/values-mcc310-mnc004-ta/strings.xml @@ -16,13 +16,9 @@ - - - - - - - - + "இணைப்பு முறைக்கு இணைய இணைப்பு இல்லை" + "சாதனங்களால் இணைய முடியவில்லை" + "இணைப்பு முறையை ஆஃப் செய்" + "ஹாட்ஸ்பாட் அல்லது இணைப்பு முறை ஆன் செய்யப்பட்டுள்ளது" "ரோமிங்கின்போது கூடுதல் கட்டணங்கள் விதிக்கப்படக்கூடும்" diff --git a/Tethering/res/values-mcc311-mnc480-ne/strings.xml b/Tethering/res/values-mcc311-mnc480-ne/strings.xml index 617c50dd0c..0a0aa217c3 100644 --- a/Tethering/res/values-mcc311-mnc480-ne/strings.xml +++ b/Tethering/res/values-mcc311-mnc480-ne/strings.xml @@ -16,13 +16,9 @@ - - - - - - - - + "Tethering has no internet" + "यन्त्रहरू कनेक्ट गर्न सकिएन" + "टेदरिङ निष्क्रिय पार्नुहोस्" + "हटस्पट वा टेदरिङ सक्रिय छ" "रोमिङ सेवा प्रयोग गर्दा अतिरिक्त शुल्क लाग्न सक्छ" diff --git a/Tethering/res/values-mcc311-mnc480-ta/strings.xml b/Tethering/res/values-mcc311-mnc480-ta/strings.xml index 0e437593ee..2ea2467e58 100644 --- a/Tethering/res/values-mcc311-mnc480-ta/strings.xml +++ b/Tethering/res/values-mcc311-mnc480-ta/strings.xml @@ -16,13 +16,9 @@ - - - - - - - - + "இணைப்பு முறைக்கு இணைய இணைப்பு இல்லை" + "சாதனங்களால் இணைய முடியவில்லை" + "இணைப்பு முறையை ஆஃப் செய்" + "ஹாட்ஸ்பாட் அல்லது இணைப்பு முறை ஆன் செய்யப்பட்டுள்ளது" "ரோமிங்கின்போது கூடுதல் கட்டணங்கள் விதிக்கப்படக்கூடும்" From 27cdaf679a83f1764b2cc3954dffd6e1b621e8f9 Mon Sep 17 00:00:00 2001 From: Mark Chien Date: Mon, 27 Apr 2020 09:12:37 +0000 Subject: [PATCH 114/188] Unbreak tethering for no offload supported devices Catch NoSuchElementException to unbreak no offload devices. To consistent with fetching offload config service, retry fetcheing offload control service. b/152430668#comment4 assert that the fetch will be retried only if the service is installed on the device. Bug: 155026033 Test: run TetheringCoverageTests in virtual devices(do not support offload) Merged-In: Ie0a32a9062c722327a27c6de13e3bb8d9588bebb Change-Id: Ie0a32a9062c722327a27c6de13e3bb8d9588bebb --- .../networkstack/tethering/OffloadHardwareInterface.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java b/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java index 55344fc75d..c4a1078d0b 100644 --- a/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java +++ b/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java @@ -41,6 +41,7 @@ import java.io.IOException; import java.net.SocketAddress; import java.net.SocketException; import java.util.ArrayList; +import java.util.NoSuchElementException; /** @@ -143,7 +144,7 @@ public class OffloadHardwareInterface { IOffloadConfig offloadConfig; try { offloadConfig = IOffloadConfig.getService(true /*retry*/); - } catch (RemoteException e) { + } catch (RemoteException | NoSuchElementException e) { mLog.e("getIOffloadConfig error " + e); return false; } @@ -239,8 +240,8 @@ public class OffloadHardwareInterface { if (mOffloadControl == null) { try { - mOffloadControl = IOffloadControl.getService(); - } catch (RemoteException e) { + mOffloadControl = IOffloadControl.getService(true /*retry*/); + } catch (RemoteException | NoSuchElementException e) { mLog.e("tethering offload control not supported: " + e); return false; } From ed50636b60e600613d34b292235ae43a777c6f80 Mon Sep 17 00:00:00 2001 From: Mark Chien Date: Mon, 27 Apr 2020 11:17:28 +0000 Subject: [PATCH 115/188] Remove Preconditions usage to stop dependecy with non-updatble class Stop depending on Preconditions that is not released on the same cadence as the module, and is maintained as part of the framework. Bug: 148636687 Test: atest TetheringTests NetworkStackNextTests Merged-In: Id0dcec44f362f79bc8c046d722635687a7388aa2 Change-Id: Id0dcec44f362f79bc8c046d722635687a7388aa2 --- Tethering/jarjar-rules.txt | 1 - .../networkstack/tethering/UpstreamNetworkMonitor.java | 5 +++-- Tethering/tests/unit/jarjar-rules.txt | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Tethering/jarjar-rules.txt b/Tethering/jarjar-rules.txt index c6efa41e58..e90a2ccaa2 100644 --- a/Tethering/jarjar-rules.txt +++ b/Tethering/jarjar-rules.txt @@ -8,7 +8,6 @@ rule com.android.internal.util.BitUtils* com.android.networkstack.tethering.util rule com.android.internal.util.IndentingPrintWriter.java* com.android.networkstack.tethering.util.IndentingPrintWriter.java@1 rule com.android.internal.util.IState.java* com.android.networkstack.tethering.util.IState.java@1 rule com.android.internal.util.MessageUtils* com.android.networkstack.tethering.util.MessageUtils@1 -rule com.android.internal.util.Preconditions* com.android.networkstack.tethering.util.Preconditions@1 rule com.android.internal.util.State* com.android.networkstack.tethering.util.State@1 rule com.android.internal.util.StateMachine* com.android.networkstack.tethering.util.StateMachine@1 rule com.android.internal.util.TrafficStatsConstants* com.android.networkstack.tethering.util.TrafficStatsConstants@1 diff --git a/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java b/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java index 25ddce4404..320427c393 100644 --- a/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java +++ b/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java @@ -43,7 +43,6 @@ import android.util.Log; import android.util.SparseIntArray; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.Preconditions; import com.android.internal.util.StateMachine; import java.util.HashMap; @@ -591,7 +590,9 @@ public class UpstreamNetworkMonitor { // Map from type to transports. final int notFound = -1; final int transport = sLegacyTypeToTransport.get(type, notFound); - Preconditions.checkArgument(transport != notFound, "unknown legacy type: " + type); + if (transport == notFound) { + throw new IllegalArgumentException("unknown legacy type: " + type); + } builder.addTransportType(transport); if (type == TYPE_MOBILE_DUN) { diff --git a/Tethering/tests/unit/jarjar-rules.txt b/Tethering/tests/unit/jarjar-rules.txt index 921fbed373..1ea56cdf1a 100644 --- a/Tethering/tests/unit/jarjar-rules.txt +++ b/Tethering/tests/unit/jarjar-rules.txt @@ -4,7 +4,6 @@ rule com.android.internal.util.BitUtils* com.android.networkstack.tethering.util rule com.android.internal.util.IndentingPrintWriter.java* com.android.networkstack.tethering.util.IndentingPrintWriter.java@1 rule com.android.internal.util.IState.java* com.android.networkstack.tethering.util.IState.java@1 rule com.android.internal.util.MessageUtils* com.android.networkstack.tethering.util.MessageUtils@1 -rule com.android.internal.util.Preconditions* com.android.networkstack.tethering.util.Preconditions@1 rule com.android.internal.util.State* com.android.networkstack.tethering.util.State@1 rule com.android.internal.util.StateMachine* com.android.networkstack.tethering.util.StateMachine@1 rule com.android.internal.util.TrafficStatsConstants* com.android.networkstack.tethering.util.TrafficStatsConstants@1 From a530ebb62decfaf73e19df14511b5ba46173dbce Mon Sep 17 00:00:00 2001 From: Remi NGUYEN VAN Date: Fri, 24 Apr 2020 12:19:37 +0000 Subject: [PATCH 116/188] Address comments on NetworkStack AIDL v6 Address issues found during AIDL review: - Rename clientAddr to singleClientAddr - Do not use a ParcelableBundle for notifyNetworkTested or notifyDataStallSuspected; instead use AIDL parcelables for stronger backwards compatibility guarantees. Test: atest NetworkMonitorTest ConnectivityServiceTest ConnectivityServiceIntegrationTest, manual Bug: 153500847 Merged-In: Id9b71784e5f6294d203230e57737979e063ff0f8 Change-Id: Id9b71784e5f6294d203230e57737979e063ff0f8 --- Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java | 2 +- .../src/android/net/dhcp/DhcpServingParamsParcelExtTest.java | 2 +- .../src/com/android/networkstack/tethering/TetheringTest.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java b/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java index 82a26beada..4f8ad8a221 100644 --- a/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java +++ b/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java @@ -169,7 +169,7 @@ public class DhcpServingParamsParcelExt extends DhcpServingParamsParcel { *

If not set, the default value is null. */ public DhcpServingParamsParcelExt setSingleClientAddr(@Nullable Inet4Address clientAddr) { - this.clientAddr = clientAddr == null ? 0 : inet4AddressToIntHTH(clientAddr); + this.singleClientAddr = clientAddr == null ? 0 : inet4AddressToIntHTH(clientAddr); return this; } diff --git a/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java b/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java index f8eb1476ba..a8857b2e5c 100644 --- a/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java +++ b/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java @@ -110,7 +110,7 @@ public class DhcpServingParamsParcelExtTest { @Test public void testSetClientAddr() { mParcel.setSingleClientAddr(TEST_CLIENT_ADDRESS); - assertEquals(TEST_CLIENT_ADDRESS_PARCELED, mParcel.clientAddr); + assertEquals(TEST_CLIENT_ADDRESS_PARCELED, mParcel.singleClientAddr); } private static Inet4Address inet4Addr(String addr) { 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 0c86eeb6cd..0363f5f998 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java @@ -1689,7 +1689,7 @@ public class TetheringTest { final DhcpServingParamsParcel params = dhcpParamsCaptor.getValue(); assertEquals(serverAddr, intToInet4AddressHTH(params.serverAddr).getHostAddress()); assertEquals(24, params.serverAddrPrefixLength); - assertEquals(clientAddrParceled, params.clientAddr); + assertEquals(clientAddrParceled, params.singleClientAddr); } @Test From bf6fa99b2aebddac4ac13f65fcbd73d253b2522c Mon Sep 17 00:00:00 2001 From: Junyu Lai Date: Wed, 29 Apr 2020 12:25:33 +0000 Subject: [PATCH 117/188] [SP18] Poll network stats in OffloadController to support data warning The OEM implemented tether offload does not support data warning since the HAL only tells the hardware about data limit but not warning. However, to add such interface in HAL needs OEM to comply and implement in hardware. Thus, as a short-term solution, polls network statistics from HAL and notify upper layer when it reaches the alert quota set by NetworkStatsService. Note that when CPU is sleeping, the data warning of tethering offload will not work since the polling is also suspended. Test: manual Test: atest OffloadControllerTest Bug: 149467454 Change-Id: I2467b64779b74cd5fec73b42fb303584f52cb1cb Merged-In: I2467b64779b74cd5fec73b42fb303584f52cb1cb (cherry picked from commit 93660e382c7717c4a3e05dafdf917654fababeae) --- .../tethering/OffloadController.java | 74 +++++++++++++++++++ .../tethering/OffloadHardwareInterface.java | 1 - 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/Tethering/src/com/android/networkstack/tethering/OffloadController.java b/Tethering/src/com/android/networkstack/tethering/OffloadController.java index c007c174fe..445a09d761 100644 --- a/Tethering/src/com/android/networkstack/tethering/OffloadController.java +++ b/Tethering/src/com/android/networkstack/tethering/OffloadController.java @@ -23,6 +23,7 @@ import static android.net.NetworkStats.SET_DEFAULT; import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.UID_ALL; import static android.net.NetworkStats.UID_TETHERING; +import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED; import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED; import android.annotation.NonNull; @@ -76,6 +77,7 @@ public class OffloadController { private static final boolean DBG = false; private static final String ANYIP = "0.0.0.0"; private static final ForwardedStats EMPTY_STATS = new ForwardedStats(); + private static final int DEFAULT_PERFORM_POLL_INTERVAL_MS = 5000; @VisibleForTesting enum StatsType { @@ -115,6 +117,16 @@ public class OffloadController { // includes upstream interfaces that have a quota set. private HashMap mInterfaceQuotas = new HashMap<>(); + // Tracking remaining alert quota. Unlike limit quota is subject to interface, the alert + // quota is interface independent and global for tether offload. Note that this is only + // accessed on the handler thread and in the constructor. + private long mRemainingAlertQuota = QUOTA_UNLIMITED; + // Runnable that used to schedule the next stats poll. + private final Runnable mScheduledPollingTask = () -> { + updateStatsForCurrentUpstream(); + maybeSchedulePollingStats(); + }; + private int mNatUpdateCallbacksReceived; private int mNatUpdateNetlinkErrors; @@ -240,6 +252,7 @@ public class OffloadController { mLog.log("tethering offload started"); mNatUpdateCallbacksReceived = 0; mNatUpdateNetlinkErrors = 0; + maybeSchedulePollingStats(); } return isStarted; } @@ -255,6 +268,9 @@ public class OffloadController { mHwInterface.stopOffloadControl(); mControlInitialized = false; mConfigInitialized = false; + if (mHandler.hasCallbacks(mScheduledPollingTask)) { + mHandler.removeCallbacks(mScheduledPollingTask); + } if (wasStarted) mLog.log("tethering offload stopped"); } @@ -345,6 +361,11 @@ public class OffloadController { @Override public void onSetAlert(long quotaBytes) { // TODO: Ask offload HAL to notify alert without stopping traffic. + // Post it to handler thread since it access remaining quota bytes. + mHandler.post(() -> { + updateAlertQuota(quotaBytes); + maybeSchedulePollingStats(); + }); } } @@ -366,15 +387,66 @@ public class OffloadController { // the stats for each interface, and does not observe partial writes where rxBytes is // updated and txBytes is not. ForwardedStats diff = mHwInterface.getForwardedStats(iface); + final long usedAlertQuota = diff.rxBytes + diff.txBytes; ForwardedStats base = mForwardedStats.get(iface); if (base != null) { diff.add(base); } + + // Update remaining alert quota if it is still positive. + if (mRemainingAlertQuota > 0 && usedAlertQuota > 0) { + // Trim to zero if overshoot. + final long newQuota = Math.max(mRemainingAlertQuota - usedAlertQuota, 0); + updateAlertQuota(newQuota); + } + mForwardedStats.put(iface, diff); // diff is a new object, just created by getForwardedStats(). Therefore, anyone reading from // mForwardedStats (i.e., any caller of getTetherStats) will see the new stats immediately. } + /** + * Update remaining alert quota, fire the {@link NetworkStatsProvider#notifyAlertReached()} + * callback when it reaches zero. This can be invoked either from service setting the alert, or + * {@code maybeUpdateStats} when updating stats. Note that this can be only called on + * handler thread. + * + * @param newQuota non-negative value to indicate the new quota, or + * {@link NetworkStatsProvider#QUOTA_UNLIMITED} to indicate there is no + * quota. + */ + private void updateAlertQuota(long newQuota) { + if (newQuota < QUOTA_UNLIMITED) { + throw new IllegalArgumentException("invalid quota value " + newQuota); + } + if (mRemainingAlertQuota == newQuota) return; + + mRemainingAlertQuota = newQuota; + if (mRemainingAlertQuota == 0) { + mLog.i("notifyAlertReached"); + if (mStatsProvider != null) mStatsProvider.notifyAlertReached(); + } + } + + /** + * Schedule polling if needed, this will be stopped if offload has been + * stopped or remaining quota reaches zero or upstream is empty. + * Note that this can be only called on handler thread. + */ + private void maybeSchedulePollingStats() { + if (!isPollingStatsNeeded()) return; + + if (mHandler.hasCallbacks(mScheduledPollingTask)) { + mHandler.removeCallbacks(mScheduledPollingTask); + } + mHandler.postDelayed(mScheduledPollingTask, DEFAULT_PERFORM_POLL_INTERVAL_MS); + } + + private boolean isPollingStatsNeeded() { + return started() && mRemainingAlertQuota > 0 + && !TextUtils.isEmpty(currentUpstreamInterface()); + } + private boolean maybeUpdateDataLimit(String iface) { // setDataLimit may only be called while offload is occurring on this upstream. if (!started() || !TextUtils.equals(iface, currentUpstreamInterface())) { @@ -414,6 +486,8 @@ public class OffloadController { final String iface = currentUpstreamInterface(); if (!TextUtils.isEmpty(iface)) mForwardedStats.putIfAbsent(iface, EMPTY_STATS); + maybeSchedulePollingStats(); + // TODO: examine return code and decide what to do if programming // upstream parameters fails (probably just wait for a subsequent // onOffloadEvent() callback to tell us offload is available again and diff --git a/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java b/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java index c4a1078d0b..293f8eae32 100644 --- a/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java +++ b/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java @@ -308,7 +308,6 @@ public class OffloadHardwareInterface { return stats; } - mLog.log(logmsg + YIELDS + stats); return stats; } From 40092b1fa75292d9b9641f5e831093272663ccdc Mon Sep 17 00:00:00 2001 From: Treehugger Robot Date: Wed, 29 Apr 2020 13:28:12 +0000 Subject: [PATCH 118/188] [SP18.1] add dependency object to OffloadController In order to mock constant in unit test, a dependency object is introduced with minimum code change to achieve this. Test: atest TetheringTests Bug: 149467454 Change-Id: I38628daddcb7be7c74846e78d36dc88f065b97d9 Merged-In: I38628daddcb7be7c74846e78d36dc88f065b97d9 (cherry picked from commit 29aee20bfafce3ca8a5477398b953a06fa2d4823) --- .../tethering/OffloadController.java | 17 +++++++++++++++-- .../networkstack/tethering/Tethering.java | 2 +- .../tethering/OffloadControllerTest.java | 8 +++++++- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/Tethering/src/com/android/networkstack/tethering/OffloadController.java b/Tethering/src/com/android/networkstack/tethering/OffloadController.java index 445a09d761..1817f35f1d 100644 --- a/Tethering/src/com/android/networkstack/tethering/OffloadController.java +++ b/Tethering/src/com/android/networkstack/tethering/OffloadController.java @@ -130,8 +130,20 @@ public class OffloadController { private int mNatUpdateCallbacksReceived; private int mNatUpdateNetlinkErrors; + @NonNull + private final Dependencies mDeps; + + // TODO: Put more parameters in constructor into dependency object. + static class Dependencies { + int getPerformPollInterval() { + // TODO: Consider make this configurable. + return DEFAULT_PERFORM_POLL_INTERVAL_MS; + } + } + public OffloadController(Handler h, OffloadHardwareInterface hwi, - ContentResolver contentResolver, NetworkStatsManager nsm, SharedLog log) { + ContentResolver contentResolver, NetworkStatsManager nsm, SharedLog log, + @NonNull Dependencies deps) { mHandler = h; mHwInterface = hwi; mContentResolver = contentResolver; @@ -147,6 +159,7 @@ public class OffloadController { provider = null; } mStatsProvider = provider; + mDeps = deps; } /** Start hardware offload. */ @@ -439,7 +452,7 @@ public class OffloadController { if (mHandler.hasCallbacks(mScheduledPollingTask)) { mHandler.removeCallbacks(mScheduledPollingTask); } - mHandler.postDelayed(mScheduledPollingTask, DEFAULT_PERFORM_POLL_INTERVAL_MS); + mHandler.postDelayed(mScheduledPollingTask, mDeps.getPerformPollInterval()); } private boolean isPollingStatsNeeded() { diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java index 4e16c49caa..0a95a5e007 100644 --- a/Tethering/src/com/android/networkstack/tethering/Tethering.java +++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java @@ -273,7 +273,7 @@ public class Tethering { mHandler = mTetherMasterSM.getHandler(); mOffloadController = new OffloadController(mHandler, mDeps.getOffloadHardwareInterface(mHandler, mLog), mContext.getContentResolver(), - statsManager, mLog); + statsManager, mLog, new OffloadController.Dependencies()); mUpstreamNetworkMonitor = mDeps.getUpstreamNetworkMonitor(mContext, mTetherMasterSM, mLog, TetherMasterSM.EVENT_UPSTREAM_CALLBACK); mForwardedDownstreams = new LinkedHashSet<>(); diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java index 65797200fa..088a663190 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java @@ -116,6 +116,12 @@ public class OffloadControllerTest { private final ArgumentCaptor mControlCallbackCaptor = ArgumentCaptor.forClass(OffloadHardwareInterface.ControlCallback.class); private MockContentResolver mContentResolver; + private OffloadController.Dependencies mDeps = new OffloadController.Dependencies() { + @Override + int getPerformPollInterval() { + return 0; + } + }; @Before public void setUp() { MockitoAnnotations.initMocks(this); @@ -150,7 +156,7 @@ public class OffloadControllerTest { private OffloadController makeOffloadController() throws Exception { OffloadController offload = new OffloadController(new Handler(Looper.getMainLooper()), - mHardware, mContentResolver, mStatsManager, new SharedLog("test")); + mHardware, mContentResolver, mStatsManager, new SharedLog("test"), mDeps); final ArgumentCaptor tetherStatsProviderCaptor = ArgumentCaptor.forClass(OffloadController.OffloadTetheringStatsProvider.class); From dc8e0fc1a11d53e54fa2d318872d1ca85e006960 Mon Sep 17 00:00:00 2001 From: Anton Hansson Date: Thu, 30 Apr 2020 17:02:07 +0100 Subject: [PATCH 119/188] Fix tethering module lib stub default It was using the systemapi stub defaults, but should be using the module_lib default. Bug: 144149403 Test: m Change-Id: Iaab154d9d71900284d92d518a086fc1227c00d5c --- Tethering/common/TetheringLib/Android.bp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tethering/common/TetheringLib/Android.bp b/Tethering/common/TetheringLib/Android.bp index ee6b9f12f9..b520bc8808 100644 --- a/Tethering/common/TetheringLib/Android.bp +++ b/Tethering/common/TetheringLib/Android.bp @@ -134,5 +134,5 @@ java_library { java_library { name: "framework-tethering-stubs-module_libs_api", srcs: [":framework-tethering-stubs-srcs-module_libs_api"], - defaults: ["framework-module-stubs-lib-defaults-systemapi"], + defaults: ["framework-module-stubs-lib-defaults-module_libs_api"], } From d7f1fabc94776b2b8e043a94618af4a17d33ac6e Mon Sep 17 00:00:00 2001 From: Anton Hansson Date: Thu, 30 Apr 2020 14:26:22 +0100 Subject: [PATCH 120/188] Rename module dist files This makes the filenames of the disted artifacts (api txts and stubs) match the module name of the modules they're from. This matches the naming scheme used by java_sdk_library, which should make the future transition to this build rule easier. Bug: 149293194 Test: lunch sdk_phone_armv7 && m sdk dist && find out/dist/apistubs Change-Id: I076f30931bf2524d57703873cd7de25b3f23b457 --- Tethering/common/TetheringLib/Android.bp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Tethering/common/TetheringLib/Android.bp b/Tethering/common/TetheringLib/Android.bp index b520bc8808..8ae1593949 100644 --- a/Tethering/common/TetheringLib/Android.bp +++ b/Tethering/common/TetheringLib/Android.bp @@ -67,6 +67,7 @@ java_library { stubs_defaults { name: "framework-tethering-stubs-defaults", srcs: [":framework-tethering-srcs"], + dist: { dest: "framework-tethering.txt" }, } filegroup { @@ -123,16 +124,19 @@ java_library { name: "framework-tethering-stubs-publicapi", srcs: [":framework-tethering-stubs-srcs-publicapi"], defaults: ["framework-module-stubs-lib-defaults-publicapi"], + dist: { dest: "framework-tethering.jar" }, } java_library { name: "framework-tethering-stubs-systemapi", srcs: [":framework-tethering-stubs-srcs-systemapi"], defaults: ["framework-module-stubs-lib-defaults-systemapi"], + dist: { dest: "framework-tethering.jar" }, } java_library { name: "framework-tethering-stubs-module_libs_api", srcs: [":framework-tethering-stubs-srcs-module_libs_api"], defaults: ["framework-module-stubs-lib-defaults-module_libs_api"], + dist: { dest: "framework-tethering.jar" }, } From b4c3fee27ff1ed359d84d25288fab445c7b9421d Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Thu, 30 Apr 2020 19:01:25 -0700 Subject: [PATCH 121/188] Import translations. DO NOT MERGE Auto-generated-cl: translation import Change-Id: Ib973f0e381ec0c90f5983a266341444cf919bbea --- Tethering/res/values-mcc310-mnc004-ne/strings.xml | 2 +- Tethering/res/values-mcc310-mnc004-zh-rTW/strings.xml | 6 +++--- Tethering/res/values-mcc311-mnc480-ne/strings.xml | 2 +- Tethering/res/values-mcc311-mnc480-zh-rTW/strings.xml | 6 +++--- Tethering/res/values-zh-rTW/strings.xml | 6 +++--- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Tethering/res/values-mcc310-mnc004-ne/strings.xml b/Tethering/res/values-mcc310-mnc004-ne/strings.xml index 12d6c2cfba..d074f15699 100644 --- a/Tethering/res/values-mcc310-mnc004-ne/strings.xml +++ b/Tethering/res/values-mcc310-mnc004-ne/strings.xml @@ -16,7 +16,7 @@ - "Tethering has no internet" + "टेदरिङमार्फत इन्टरनेट कनेक्सन प्राप्त हुन सकेन" "यन्त्रहरू कनेक्ट गर्न सकिएन" "टेदरिङ निष्क्रिय पार्नुहोस्" "हटस्पट वा टेदरिङ सक्रिय छ" diff --git a/Tethering/res/values-mcc310-mnc004-zh-rTW/strings.xml b/Tethering/res/values-mcc310-mnc004-zh-rTW/strings.xml index 05b90692ea..528a1e5292 100644 --- a/Tethering/res/values-mcc310-mnc004-zh-rTW/strings.xml +++ b/Tethering/res/values-mcc310-mnc004-zh-rTW/strings.xml @@ -16,9 +16,9 @@ - "無法透過數據連線連上網際網路" + "無法透過網路共用連上網際網路" "裝置無法連線" - "關閉數據連線" - "無線基地台或數據連線已開啟" + "關閉網路共用" + "無線基地台或網路共用已開啟" "使用漫遊服務可能須支付額外費用" diff --git a/Tethering/res/values-mcc311-mnc480-ne/strings.xml b/Tethering/res/values-mcc311-mnc480-ne/strings.xml index 0a0aa217c3..1503244f50 100644 --- a/Tethering/res/values-mcc311-mnc480-ne/strings.xml +++ b/Tethering/res/values-mcc311-mnc480-ne/strings.xml @@ -16,7 +16,7 @@ - "Tethering has no internet" + "टेदरिङमार्फत इन्टरनेट कनेक्सन प्राप्त हुन सकेन" "यन्त्रहरू कनेक्ट गर्न सकिएन" "टेदरिङ निष्क्रिय पार्नुहोस्" "हटस्पट वा टेदरिङ सक्रिय छ" diff --git a/Tethering/res/values-mcc311-mnc480-zh-rTW/strings.xml b/Tethering/res/values-mcc311-mnc480-zh-rTW/strings.xml index ea01b943fb..cd653df1da 100644 --- a/Tethering/res/values-mcc311-mnc480-zh-rTW/strings.xml +++ b/Tethering/res/values-mcc311-mnc480-zh-rTW/strings.xml @@ -16,9 +16,9 @@ - "無法透過數據連線連上網際網路" + "無法透過網路共用連上網際網路" "裝置無法連線" - "關閉數據連線" - "無線基地台或數據連線已開啟" + "關閉網路共用" + "無線基地台或網路共用已開啟" "使用漫遊服務可能須支付額外費用" diff --git a/Tethering/res/values-zh-rTW/strings.xml b/Tethering/res/values-zh-rTW/strings.xml index 9d738a76eb..50a50bf7a9 100644 --- a/Tethering/res/values-zh-rTW/strings.xml +++ b/Tethering/res/values-zh-rTW/strings.xml @@ -16,11 +16,11 @@ - "數據連線或無線基地台已啟用" + "網路共用或無線基地台已啟用" "輕觸即可進行設定。" - "數據連線已停用" + "網路共用已停用" "詳情請洽你的管理員" - "無線基地台與數據連線狀態" + "無線基地台與網路共用狀態" From b7011d22653cf7b564e0ba25bc35da65e1b879f9 Mon Sep 17 00:00:00 2001 From: junyulai Date: Fri, 24 Apr 2020 15:47:24 +0800 Subject: [PATCH 122/188] [SP18.3] Adapt TestableNetworkStatsProviderCbBinder This is a no-op refactoring to adapt new test provider callback. Also this patch adapts TestLooper to allow better control on delay messages that will be verified in subsequent tests. Test: atest OffloadControllerTest Bug: 149467454 Change-Id: Icfd6ff289d6689ae2d5753d3fe472516c808dc7a Merged-In: Icfd6ff289d6689ae2d5753d3fe472516c808dc7a (cherry-picked from aosp/1295346) --- .../tethering/OffloadControllerTest.java | 53 ++++++++----------- 1 file changed, 21 insertions(+), 32 deletions(-) diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java index 088a663190..6d7c4284a7 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java @@ -31,13 +31,12 @@ import static com.android.networkstack.tethering.OffloadController.StatsType.STA import static com.android.networkstack.tethering.OffloadHardwareInterface.ForwardedStats; import static com.android.testutils.MiscAssertsKt.assertContainsAll; import static com.android.testutils.MiscAssertsKt.assertThrows; -import static com.android.testutils.NetworkStatsUtilsKt.orderInsensitiveEquals; +import static com.android.testutils.NetworkStatsUtilsKt.assertNetworkStatsEquals; import static junit.framework.Assert.assertNotNull; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.anyObject; @@ -46,7 +45,6 @@ import static org.mockito.Matchers.eq; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -63,10 +61,9 @@ import android.net.LinkProperties; import android.net.NetworkStats; import android.net.NetworkStats.Entry; import android.net.RouteInfo; -import android.net.netstats.provider.INetworkStatsProviderCallback; import android.net.util.SharedLog; import android.os.Handler; -import android.os.Looper; +import android.os.test.TestLooper; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.test.mock.MockContentResolver; @@ -75,7 +72,7 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.test.FakeSettingsProvider; -import com.android.testutils.HandlerUtilsKt; +import com.android.testutils.TestableNetworkStatsProviderCbBinder; import org.junit.After; import org.junit.Before; @@ -109,13 +106,15 @@ public class OffloadControllerTest { @Mock private ApplicationInfo mApplicationInfo; @Mock private Context mContext; @Mock private NetworkStatsManager mStatsManager; - @Mock private INetworkStatsProviderCallback mTetherStatsProviderCb; + // Late init since methods must be called by the thread that created this object. + private TestableNetworkStatsProviderCbBinder mTetherStatsProviderCb; private OffloadController.OffloadTetheringStatsProvider mTetherStatsProvider; private final ArgumentCaptor mStringArrayCaptor = ArgumentCaptor.forClass(ArrayList.class); private final ArgumentCaptor mControlCallbackCaptor = ArgumentCaptor.forClass(OffloadHardwareInterface.ControlCallback.class); private MockContentResolver mContentResolver; + private final TestLooper mTestLooper = new TestLooper(); private OffloadController.Dependencies mDeps = new OffloadController.Dependencies() { @Override int getPerformPollInterval() { @@ -151,11 +150,11 @@ public class OffloadControllerTest { } private void waitForIdle() { - HandlerUtilsKt.waitForIdle(new Handler(Looper.getMainLooper()), WAIT_FOR_IDLE_TIMEOUT); + mTestLooper.dispatchAll(); } private OffloadController makeOffloadController() throws Exception { - OffloadController offload = new OffloadController(new Handler(Looper.getMainLooper()), + OffloadController offload = new OffloadController(new Handler(mTestLooper.getLooper()), mHardware, mContentResolver, mStatsManager, new SharedLog("test"), mDeps); final ArgumentCaptor tetherStatsProviderCaptor = @@ -164,6 +163,7 @@ public class OffloadControllerTest { tetherStatsProviderCaptor.capture()); mTetherStatsProvider = tetherStatsProviderCaptor.getValue(); assertNotNull(mTetherStatsProvider); + mTetherStatsProviderCb = new TestableNetworkStatsProviderCbBinder(); mTetherStatsProvider.setProviderCallbackBinder(mTetherStatsProviderCb); return offload; } @@ -459,20 +459,12 @@ public class OffloadControllerTest { .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999)) .addEntry(buildTestEntry(STATS_PER_UID, ethernetIface, 12345, 54321)); - assertTrue(orderInsensitiveEquals(expectedIfaceStats, ifaceStats)); - assertTrue(orderInsensitiveEquals(expectedUidStats, uidStats)); - - final ArgumentCaptor ifaceStatsCaptor = ArgumentCaptor.forClass( - NetworkStats.class); - final ArgumentCaptor uidStatsCaptor = ArgumentCaptor.forClass( - NetworkStats.class); + assertNetworkStatsEquals(expectedIfaceStats, ifaceStats); + assertNetworkStatsEquals(expectedUidStats, uidStats); // Force pushing stats update to verify the stats reported. mTetherStatsProvider.pushTetherStats(); - verify(mTetherStatsProviderCb, times(1)) - .notifyStatsUpdated(anyInt(), ifaceStatsCaptor.capture(), uidStatsCaptor.capture()); - assertTrue(orderInsensitiveEquals(expectedIfaceStats, ifaceStatsCaptor.getValue())); - assertTrue(orderInsensitiveEquals(expectedUidStats, uidStatsCaptor.getValue())); + mTetherStatsProviderCb.expectNotifyStatsUpdated(expectedIfaceStats, expectedUidStats); when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn( new ForwardedStats(100000, 100000)); @@ -498,11 +490,10 @@ public class OffloadControllerTest { .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999)) .addEntry(buildTestEntry(STATS_PER_UID, ethernetIface, 112345, 154321)); - assertTrue(orderInsensitiveEquals(expectedIfaceStatsAccu, ifaceStatsAccu)); - assertTrue(orderInsensitiveEquals(expectedUidStatsAccu, uidStatsAccu)); + assertNetworkStatsEquals(expectedIfaceStatsAccu, ifaceStatsAccu); + assertNetworkStatsEquals(expectedUidStatsAccu, uidStatsAccu); // Verify that only diff of stats is reported. - reset(mTetherStatsProviderCb); mTetherStatsProvider.pushTetherStats(); final NetworkStats expectedIfaceStatsDiff = new NetworkStats(0L, 2) .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 0, 0)) @@ -511,10 +502,8 @@ public class OffloadControllerTest { final NetworkStats expectedUidStatsDiff = new NetworkStats(0L, 2) .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 0, 0)) .addEntry(buildTestEntry(STATS_PER_UID, ethernetIface, 100000, 100000)); - verify(mTetherStatsProviderCb, times(1)) - .notifyStatsUpdated(anyInt(), ifaceStatsCaptor.capture(), uidStatsCaptor.capture()); - assertTrue(orderInsensitiveEquals(expectedIfaceStatsDiff, ifaceStatsCaptor.getValue())); - assertTrue(orderInsensitiveEquals(expectedUidStatsDiff, uidStatsCaptor.getValue())); + mTetherStatsProviderCb.expectNotifyStatsUpdated(expectedIfaceStatsDiff, + expectedUidStatsDiff); } @Test @@ -591,7 +580,7 @@ public class OffloadControllerTest { OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue(); callback.onStoppedLimitReached(); - verify(mTetherStatsProviderCb, times(1)).notifyStatsUpdated(anyInt(), any(), any()); + mTetherStatsProviderCb.expectNotifyStatsUpdated(); } @Test @@ -695,8 +684,8 @@ public class OffloadControllerTest { verify(mHardware, times(1)).getForwardedStats(eq(RMNET0)); verify(mHardware, times(1)).getForwardedStats(eq(WLAN0)); // TODO: verify the exact stats reported. - verify(mTetherStatsProviderCb, times(1)).notifyStatsUpdated(anyInt(), any(), any()); - verifyNoMoreInteractions(mTetherStatsProviderCb); + mTetherStatsProviderCb.expectNotifyStatsUpdated(); + mTetherStatsProviderCb.assertNoCallback(); verifyNoMoreInteractions(mHardware); } @@ -760,8 +749,8 @@ public class OffloadControllerTest { // Verify forwarded stats behaviour. verify(mHardware, times(1)).getForwardedStats(eq(RMNET0)); verify(mHardware, times(1)).getForwardedStats(eq(WLAN0)); - verify(mTetherStatsProviderCb, times(1)).notifyStatsUpdated(anyInt(), any(), any()); - verifyNoMoreInteractions(mTetherStatsProviderCb); + mTetherStatsProviderCb.expectNotifyStatsUpdated(); + mTetherStatsProviderCb.assertNoCallback(); // TODO: verify local prefixes and downstreams are also pushed to the HAL. verify(mHardware, times(1)).setLocalPrefixes(mStringArrayCaptor.capture()); From 14722baf0dfbd2bd76890c07d5ebe95118e3b707 Mon Sep 17 00:00:00 2001 From: junyulai Date: Fri, 24 Apr 2020 19:29:27 +0800 Subject: [PATCH 123/188] [SP18.4] Add unit test for polling network stats in OffloadController Test: atest OffloadControllerTest Bug: 149467454 Change-Id: I9b9c9c096a2366aaf383d5c2d567db6682f02dad Merged-In: I9b9c9c096a2366aaf383d5c2d567db6682f02dad (cherry-picked from aosp/1295347) --- .../tethering/OffloadController.java | 3 +- .../tethering/OffloadControllerTest.java | 60 ++++++++++++++++++- 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/Tethering/src/com/android/networkstack/tethering/OffloadController.java b/Tethering/src/com/android/networkstack/tethering/OffloadController.java index 1817f35f1d..c0638636bb 100644 --- a/Tethering/src/com/android/networkstack/tethering/OffloadController.java +++ b/Tethering/src/com/android/networkstack/tethering/OffloadController.java @@ -77,7 +77,8 @@ public class OffloadController { private static final boolean DBG = false; private static final String ANYIP = "0.0.0.0"; private static final ForwardedStats EMPTY_STATS = new ForwardedStats(); - private static final int DEFAULT_PERFORM_POLL_INTERVAL_MS = 5000; + @VisibleForTesting + static final int DEFAULT_PERFORM_POLL_INTERVAL_MS = 5000; @VisibleForTesting enum StatsType { diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java index 6d7c4284a7..f71cd211b2 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java @@ -26,6 +26,7 @@ import static android.net.NetworkStats.UID_TETHERING; import static android.net.RouteInfo.RTN_UNICAST; import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED; +import static com.android.networkstack.tethering.OffloadController.DEFAULT_PERFORM_POLL_INTERVAL_MS; import static com.android.networkstack.tethering.OffloadController.StatsType.STATS_PER_IFACE; import static com.android.networkstack.tethering.OffloadController.StatsType.STATS_PER_UID; import static com.android.networkstack.tethering.OffloadHardwareInterface.ForwardedStats; @@ -45,6 +46,7 @@ import static org.mockito.Matchers.eq; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -61,6 +63,7 @@ import android.net.LinkProperties; import android.net.NetworkStats; import android.net.NetworkStats.Entry; import android.net.RouteInfo; +import android.net.netstats.provider.NetworkStatsProvider; import android.net.util.SharedLog; import android.os.Handler; import android.os.test.TestLooper; @@ -118,7 +121,7 @@ public class OffloadControllerTest { private OffloadController.Dependencies mDeps = new OffloadController.Dependencies() { @Override int getPerformPollInterval() { - return 0; + return -1; // Disabled. } }; @@ -149,6 +152,15 @@ public class OffloadControllerTest { Settings.Global.putInt(mContentResolver, TETHER_OFFLOAD_DISABLED, 0); } + private void setOffloadPollInterval(int interval) { + mDeps = new OffloadController.Dependencies() { + @Override + int getPerformPollInterval() { + return interval; + } + }; + } + private void waitForIdle() { mTestLooper.dispatchAll(); } @@ -769,4 +781,50 @@ public class OffloadControllerTest { verifyNoMoreInteractions(mHardware); } + @Test + public void testOnSetAlert() throws Exception { + setupFunctioningHardwareInterface(); + enableOffload(); + setOffloadPollInterval(DEFAULT_PERFORM_POLL_INTERVAL_MS); + final OffloadController offload = makeOffloadController(); + offload.start(); + + // Initialize with fake eth upstream. + final String ethernetIface = "eth1"; + InOrder inOrder = inOrder(mHardware); + final LinkProperties lp = new LinkProperties(); + lp.setInterfaceName(ethernetIface); + offload.setUpstreamLinkProperties(lp); + // Previous upstream was null, so no stats are fetched. + inOrder.verify(mHardware, never()).getForwardedStats(any()); + + // Verify that set quota to 0 will immediately triggers an callback. + mTetherStatsProvider.onSetAlert(0); + waitForIdle(); + mTetherStatsProviderCb.expectNotifyAlertReached(); + + // Verify that notifyAlertReached never fired if quota is not yet reached. + when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn( + new ForwardedStats(0, 0)); + mTetherStatsProvider.onSetAlert(100); + mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS); + waitForIdle(); + mTetherStatsProviderCb.assertNoCallback(); + + // Verify that notifyAlertReached fired when quota is reached. + when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn( + new ForwardedStats(50, 50)); + mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS); + waitForIdle(); + mTetherStatsProviderCb.expectNotifyAlertReached(); + + // Verify that set quota with UNLIMITED won't trigger any callback, and won't fetch + // any stats since the polling is stopped. + reset(mHardware); + mTetherStatsProvider.onSetAlert(NetworkStatsProvider.QUOTA_UNLIMITED); + mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS); + waitForIdle(); + mTetherStatsProviderCb.assertNoCallback(); + verify(mHardware, never()).getForwardedStats(any()); + } } From 2a5f21425a15684075d5bb6edddc41944308460b Mon Sep 17 00:00:00 2001 From: junyulai Date: Thu, 30 Apr 2020 15:21:55 +0800 Subject: [PATCH 124/188] [SP18.5] Create offload controller poll interval to resource Test: atest TetheringConfigurationTest Bug: 149467454 Change-Id: I8b4ad920a4945504914d3741a9fba5c096fbf452 Merged-In: I8b4ad920a4945504914d3741a9fba5c096fbf452 (cherry-picked from aosp/1299413) --- Tethering/res/values/config.xml | 7 +++++++ Tethering/res/values/overlayable.xml | 1 + .../tethering/TetheringConfiguration.java | 20 +++++++++++++++++++ .../tethering/TetheringConfigurationTest.java | 19 ++++++++++++++++++ 4 files changed, 47 insertions(+) diff --git a/Tethering/res/values/config.xml b/Tethering/res/values/config.xml index 83c99d22fd..9dda7166a2 100644 --- a/Tethering/res/values/config.xml +++ b/Tethering/res/values/config.xml @@ -64,6 +64,13 @@ + + 5000 + diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java index aeac437e24..9d4e747327 100644 --- a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java @@ -78,6 +78,12 @@ public class TetheringConfiguration { public static final String TETHER_ENABLE_LEGACY_DHCP_SERVER = "tether_enable_legacy_dhcp_server"; + /** + * Default value that used to periodic polls tether offload stats from tethering offload HAL + * to make the data warnings work. + */ + public static final int DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS = 5000; + public final String[] tetherableUsbRegexs; public final String[] tetherableWifiRegexs; public final String[] tetherableWifiP2pRegexs; @@ -96,6 +102,8 @@ public class TetheringConfiguration { public final int activeDataSubId; + private final int mOffloadPollInterval; + public TetheringConfiguration(Context ctx, SharedLog log, int id) { final SharedLog configLog = log.forSubComponent("config"); @@ -129,6 +137,10 @@ public class TetheringConfiguration { R.integer.config_mobile_hotspot_provision_check_period, 0 /* No periodic re-check */); + mOffloadPollInterval = getResourceInteger(res, + R.integer.config_tether_offload_poll_interval, + DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); + configLog.log(toString()); } @@ -189,6 +201,9 @@ public class TetheringConfiguration { dumpStringArray(pw, "legacyDhcpRanges", legacyDhcpRanges); dumpStringArray(pw, "defaultIPv4DNS", defaultIPv4DNS); + pw.print("offloadPollInterval: "); + pw.println(mOffloadPollInterval); + dumpStringArray(pw, "provisioningApp", provisioningApp); pw.print("provisioningAppNoUi: "); pw.println(provisioningAppNoUi); @@ -208,6 +223,7 @@ public class TetheringConfiguration { makeString(tetherableBluetoothRegexs))); sj.add(String.format("isDunRequired:%s", isDunRequired)); sj.add(String.format("chooseUpstreamAutomatically:%s", chooseUpstreamAutomatically)); + sj.add(String.format("offloadPollInterval:%d", mOffloadPollInterval)); sj.add(String.format("preferredUpstreamIfaceTypes:%s", toIntArray(preferredUpstreamIfaceTypes))); sj.add(String.format("provisioningApp:%s", makeString(provisioningApp))); @@ -246,6 +262,10 @@ public class TetheringConfiguration { return (tm != null) ? tm.isTetheringApnRequired() : false; } + public int getOffloadPollInterval() { + return mOffloadPollInterval; + } + private static Collection getUpstreamIfaceTypes(Resources res, boolean dunRequired) { final int[] ifaceTypes = res.getIntArray(R.array.config_tether_upstream_types); final ArrayList upstreamIfaceTypes = new ArrayList<>(ifaceTypes.length); diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java index 07ddea43f4..e8ba5b8168 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java @@ -115,6 +115,8 @@ public class TetheringConfigurationTest { when(mResources.getStringArray(R.array.config_tether_dhcp_range)).thenReturn( new String[0]); + when(mResources.getInteger(R.integer.config_tether_offload_poll_interval)).thenReturn( + TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); when(mResources.getStringArray(R.array.config_tether_usb_regexs)).thenReturn(new String[0]); when(mResources.getStringArray(R.array.config_tether_wifi_regexs)) .thenReturn(new String[]{ "test_wlan\\d" }); @@ -313,6 +315,23 @@ public class TetheringConfigurationTest { assertFalse(cfg.enableLegacyDhcpServer); } + @Test + public void testOffloadIntervalByResource() { + final TetheringConfiguration intervalByDefault = + new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); + assertEquals(TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS, + intervalByDefault.getOffloadPollInterval()); + + final int[] testOverrides = {0, 3000, -1}; + for (final int override : testOverrides) { + when(mResources.getInteger(R.integer.config_tether_offload_poll_interval)).thenReturn( + override); + final TetheringConfiguration overrideByRes = + new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); + assertEquals(override, overrideByRes.getOffloadPollInterval()); + } + } + @Test public void testGetResourcesBySubId() { setUpResourceForSubId(); From e1a04c04d7c79e22a5bef406af9cc1daa28cba59 Mon Sep 17 00:00:00 2001 From: Junyu Lai Date: Tue, 5 May 2020 10:45:38 +0000 Subject: [PATCH 125/188] [SP18.6] Make offload controller poll interval configurable Test: atest TetheringTests Bug: 149467454 Change-Id: I0b07a0b520dedb479bf863fbfe898ae85b84b0f3 Merged-In: I0b07a0b520dedb479bf863fbfe898ae85b84b0f3 (cherry picked from commit 8371fa281839d5b1a1027936f2f83e59bf5301a3) --- .../tethering/OffloadController.java | 20 +++++++------- .../networkstack/tethering/Tethering.java | 8 +++++- .../tethering/OffloadControllerTest.java | 27 +++++++++---------- 3 files changed, 30 insertions(+), 25 deletions(-) diff --git a/Tethering/src/com/android/networkstack/tethering/OffloadController.java b/Tethering/src/com/android/networkstack/tethering/OffloadController.java index c0638636bb..88c77b07e7 100644 --- a/Tethering/src/com/android/networkstack/tethering/OffloadController.java +++ b/Tethering/src/com/android/networkstack/tethering/OffloadController.java @@ -26,6 +26,8 @@ import static android.net.NetworkStats.UID_TETHERING; import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED; import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED; +import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS; + import android.annotation.NonNull; import android.annotation.Nullable; import android.app.usage.NetworkStatsManager; @@ -77,8 +79,6 @@ public class OffloadController { private static final boolean DBG = false; private static final String ANYIP = "0.0.0.0"; private static final ForwardedStats EMPTY_STATS = new ForwardedStats(); - @VisibleForTesting - static final int DEFAULT_PERFORM_POLL_INTERVAL_MS = 5000; @VisibleForTesting enum StatsType { @@ -135,11 +135,9 @@ public class OffloadController { private final Dependencies mDeps; // TODO: Put more parameters in constructor into dependency object. - static class Dependencies { - int getPerformPollInterval() { - // TODO: Consider make this configurable. - return DEFAULT_PERFORM_POLL_INTERVAL_MS; - } + interface Dependencies { + @NonNull + TetheringConfiguration getTetherConfig(); } public OffloadController(Handler h, OffloadHardwareInterface hwi, @@ -453,12 +451,16 @@ public class OffloadController { if (mHandler.hasCallbacks(mScheduledPollingTask)) { mHandler.removeCallbacks(mScheduledPollingTask); } - mHandler.postDelayed(mScheduledPollingTask, mDeps.getPerformPollInterval()); + mHandler.postDelayed(mScheduledPollingTask, + mDeps.getTetherConfig().getOffloadPollInterval()); } private boolean isPollingStatsNeeded() { return started() && mRemainingAlertQuota > 0 - && !TextUtils.isEmpty(currentUpstreamInterface()); + && !TextUtils.isEmpty(currentUpstreamInterface()) + && mDeps.getTetherConfig() != null + && mDeps.getTetherConfig().getOffloadPollInterval() + >= DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS; } private boolean maybeUpdateDataLimit(String iface) { diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java index 0a95a5e007..952325cc43 100644 --- a/Tethering/src/com/android/networkstack/tethering/Tethering.java +++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java @@ -273,7 +273,13 @@ public class Tethering { mHandler = mTetherMasterSM.getHandler(); mOffloadController = new OffloadController(mHandler, mDeps.getOffloadHardwareInterface(mHandler, mLog), mContext.getContentResolver(), - statsManager, mLog, new OffloadController.Dependencies()); + statsManager, mLog, new OffloadController.Dependencies() { + + @Override + public TetheringConfiguration getTetherConfig() { + return mConfig; + } + }); mUpstreamNetworkMonitor = mDeps.getUpstreamNetworkMonitor(mContext, mTetherMasterSM, mLog, TetherMasterSM.EVENT_UPSTREAM_CALLBACK); mForwardedDownstreams = new LinkedHashSet<>(); diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java index f71cd211b2..b291438937 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java @@ -26,10 +26,10 @@ import static android.net.NetworkStats.UID_TETHERING; import static android.net.RouteInfo.RTN_UNICAST; import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED; -import static com.android.networkstack.tethering.OffloadController.DEFAULT_PERFORM_POLL_INTERVAL_MS; import static com.android.networkstack.tethering.OffloadController.StatsType.STATS_PER_IFACE; import static com.android.networkstack.tethering.OffloadController.StatsType.STATS_PER_UID; import static com.android.networkstack.tethering.OffloadHardwareInterface.ForwardedStats; +import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS; import static com.android.testutils.MiscAssertsKt.assertContainsAll; import static com.android.testutils.MiscAssertsKt.assertThrows; import static com.android.testutils.NetworkStatsUtilsKt.assertNetworkStatsEquals; @@ -109,6 +109,7 @@ public class OffloadControllerTest { @Mock private ApplicationInfo mApplicationInfo; @Mock private Context mContext; @Mock private NetworkStatsManager mStatsManager; + @Mock private TetheringConfiguration mTetherConfig; // Late init since methods must be called by the thread that created this object. private TestableNetworkStatsProviderCbBinder mTetherStatsProviderCb; private OffloadController.OffloadTetheringStatsProvider mTetherStatsProvider; @@ -120,8 +121,8 @@ public class OffloadControllerTest { private final TestLooper mTestLooper = new TestLooper(); private OffloadController.Dependencies mDeps = new OffloadController.Dependencies() { @Override - int getPerformPollInterval() { - return -1; // Disabled. + public TetheringConfiguration getTetherConfig() { + return mTetherConfig; } }; @@ -133,6 +134,7 @@ public class OffloadControllerTest { mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); when(mContext.getContentResolver()).thenReturn(mContentResolver); FakeSettingsProvider.clearSettingsProvider(); + when(mTetherConfig.getOffloadPollInterval()).thenReturn(-1); // Disabled. } @After public void tearDown() throws Exception { @@ -153,12 +155,7 @@ public class OffloadControllerTest { } private void setOffloadPollInterval(int interval) { - mDeps = new OffloadController.Dependencies() { - @Override - int getPerformPollInterval() { - return interval; - } - }; + when(mTetherConfig.getOffloadPollInterval()).thenReturn(interval); } private void waitForIdle() { @@ -364,9 +361,9 @@ public class OffloadControllerTest { stacked.setInterfaceName("stacked"); stacked.addLinkAddress(new LinkAddress("192.0.2.129/25")); stacked.addRoute(new RouteInfo(null, InetAddress.getByName("192.0.2.254"), null, - RTN_UNICAST)); + RTN_UNICAST)); stacked.addRoute(new RouteInfo(null, InetAddress.getByName("fe80::bad:f00"), null, - RTN_UNICAST)); + RTN_UNICAST)); assertTrue(lp.addStackedLink(stacked)); offload.setUpstreamLinkProperties(lp); // No change in local addresses means no call to setLocalPrefixes(). @@ -785,7 +782,7 @@ public class OffloadControllerTest { public void testOnSetAlert() throws Exception { setupFunctioningHardwareInterface(); enableOffload(); - setOffloadPollInterval(DEFAULT_PERFORM_POLL_INTERVAL_MS); + setOffloadPollInterval(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); final OffloadController offload = makeOffloadController(); offload.start(); @@ -807,14 +804,14 @@ public class OffloadControllerTest { when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn( new ForwardedStats(0, 0)); mTetherStatsProvider.onSetAlert(100); - mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS); + mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); waitForIdle(); mTetherStatsProviderCb.assertNoCallback(); // Verify that notifyAlertReached fired when quota is reached. when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn( new ForwardedStats(50, 50)); - mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS); + mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); waitForIdle(); mTetherStatsProviderCb.expectNotifyAlertReached(); @@ -822,7 +819,7 @@ public class OffloadControllerTest { // any stats since the polling is stopped. reset(mHardware); mTetherStatsProvider.onSetAlert(NetworkStatsProvider.QUOTA_UNLIMITED); - mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS); + mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); waitForIdle(); mTetherStatsProviderCb.assertNoCallback(); verify(mHardware, never()).getForwardedStats(any()); From 5b0be161ad025d03b94a13a41d1c6d9145102bd5 Mon Sep 17 00:00:00 2001 From: Anton Hansson Date: Fri, 1 May 2020 18:25:09 +0100 Subject: [PATCH 126/188] Enable api lint and check_last_api for modules This adds checking of module api compatibility to the individual module api rules. Until now, this checking has been done via the monolithic metalava runs which we are aiming to get rid of. Now is a good time to do this because we can compare them to the just finalized version 30 API, which we have no diffs with. Baseline the existing wifi failures that metalava fails to find in the previous API. Bug: 144149403 Test: m checkapi Change-Id: Id222895daa3a769c265965b052a17d5a1ca18462 --- Tethering/common/TetheringLib/Android.bp | 27 ++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Tethering/common/TetheringLib/Android.bp b/Tethering/common/TetheringLib/Android.bp index 8ae1593949..d029d2bded 100644 --- a/Tethering/common/TetheringLib/Android.bp +++ b/Tethering/common/TetheringLib/Android.bp @@ -94,6 +94,15 @@ droidstubs { "framework-module-stubs-defaults-publicapi", "framework-tethering-stubs-defaults", ], + check_api: { + last_released: { + api_file: ":framework-tethering.api.public.latest", + removed_api_file: ":framework-tethering-removed.api.public.latest", + }, + api_lint: { + new_since: ":framework-tethering.api.public.latest", + }, + }, } droidstubs { @@ -102,6 +111,15 @@ droidstubs { "framework-module-stubs-defaults-systemapi", "framework-tethering-stubs-defaults", ], + check_api: { + last_released: { + api_file: ":framework-tethering.api.system.latest", + removed_api_file: ":framework-tethering-removed.api.system.latest", + }, + api_lint: { + new_since: ":framework-tethering.api.system.latest", + }, + }, } droidstubs { @@ -110,6 +128,15 @@ droidstubs { "framework-module-api-defaults-module_libs_api", "framework-tethering-stubs-defaults", ], + check_api: { + last_released: { + api_file: ":framework-tethering.api.module-lib.latest", + removed_api_file: ":framework-tethering-removed.api.module-lib.latest", + }, + api_lint: { + new_since: ":framework-tethering.api.module-lib.latest", + }, + }, } droidstubs { From 9548c8580c0bedad6ae78f6fec96cab924dffcf2 Mon Sep 17 00:00:00 2001 From: markchien Date: Tue, 5 May 2020 17:42:44 +0800 Subject: [PATCH 127/188] Override tethering module APK-in-APEX for Go variant Bug: 155604224 Test: build Change-Id: I4147173b5f3668491ff9cb7f1f86715b036d6d4b --- Tethering/Android.bp | 1 + Tethering/apex/Android.bp | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/Tethering/Android.bp b/Tethering/Android.bp index bfb65241ec..83f761f812 100644 --- a/Tethering/Android.bp +++ b/Tethering/Android.bp @@ -109,6 +109,7 @@ android_app { manifest: "AndroidManifest_InProcess.xml", // InProcessTethering is a replacement for Tethering overrides: ["Tethering"], + apex_available: ["com.android.tethering"], } // Updatable tethering packaged as an application diff --git a/Tethering/apex/Android.bp b/Tethering/apex/Android.bp index 24df5f6960..20ccd2ad64 100644 --- a/Tethering/apex/Android.bp +++ b/Tethering/apex/Android.bp @@ -36,3 +36,12 @@ android_app_certificate { name: "com.android.tethering.certificate", certificate: "com.android.tethering", } + +override_apex { + name: "com.android.tethering.inprocess", + base: "com.android.tethering", + package_name: "com.android.tethering.inprocess", + apps: [ + "InProcessTethering", + ], +} From c96fbe0abe5baab541cc827eb7a69701ce964dce Mon Sep 17 00:00:00 2001 From: Mark Chien Date: Thu, 7 May 2020 03:49:42 +0000 Subject: [PATCH 128/188] Test tethering log dump Bug: 145490751 Test: atest TetheringTests Merged-In: I01fc6969041711f7a15880144ee5eac591086ecd Change-Id: I01fc6969041711f7a15880144ee5eac591086ecd --- .../networkstack/tethering/Tethering.java | 8 +--- .../tethering/TetheringDependencies.java | 14 +++++++ .../networkstack/tethering/TetheringTest.java | 40 +++++++++++++++++-- 3 files changed, 53 insertions(+), 9 deletions(-) diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java index 952325cc43..b2a43c47d1 100644 --- a/Tethering/src/com/android/networkstack/tethering/Tethering.java +++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java @@ -62,7 +62,6 @@ import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import static com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE; -import android.app.usage.NetworkStatsManager; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothPan; import android.bluetooth.BluetoothProfile; @@ -268,12 +267,9 @@ public class Tethering { mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper, deps); mTetherMasterSM.start(); - final NetworkStatsManager statsManager = - (NetworkStatsManager) mContext.getSystemService(Context.NETWORK_STATS_SERVICE); mHandler = mTetherMasterSM.getHandler(); - mOffloadController = new OffloadController(mHandler, - mDeps.getOffloadHardwareInterface(mHandler, mLog), mContext.getContentResolver(), - statsManager, mLog, new OffloadController.Dependencies() { + mOffloadController = mDeps.getOffloadController(mHandler, mLog, + new OffloadController.Dependencies() { @Override public TetheringConfiguration getTetherConfig() { diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java b/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java index 9b54b5ff24..802f2acb33 100644 --- a/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java @@ -16,6 +16,7 @@ package com.android.networkstack.tethering; +import android.app.usage.NetworkStatsManager; import android.bluetooth.BluetoothAdapter; import android.content.Context; import android.net.INetd; @@ -46,6 +47,19 @@ public abstract class TetheringDependencies { return new OffloadHardwareInterface(h, log); } + /** + * Get a reference to the offload controller to be used by tethering. + */ + @NonNull + public OffloadController getOffloadController(@NonNull Handler h, + @NonNull SharedLog log, @NonNull OffloadController.Dependencies deps) { + final NetworkStatsManager statsManager = + (NetworkStatsManager) getContext().getSystemService(Context.NETWORK_STATS_SERVICE); + return new OffloadController(h, getOffloadHardwareInterface(h, log), + getContext().getContentResolver(), statsManager, log, deps); + } + + /** * Get a reference to the UpstreamNetworkMonitor to be used by tethering. */ 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 0363f5f998..fff7a70f54 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java @@ -150,6 +150,8 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.io.FileDescriptor; +import java.io.PrintWriter; import java.net.Inet4Address; import java.net.Inet6Address; import java.util.ArrayList; @@ -212,6 +214,9 @@ public class TetheringTest { private Tethering mTethering; private PhoneStateListener mPhoneStateListener; private InterfaceConfigurationParcel mInterfaceConfiguration; + private TetheringConfiguration mConfig; + private EntitlementManager mEntitleMgr; + private OffloadController mOffloadCtrl; private class TestContext extends BroadcastInterceptingContext { TestContext(Context base) { @@ -297,8 +302,9 @@ public class TetheringTest { } } - private class MockTetheringConfiguration extends TetheringConfiguration { - MockTetheringConfiguration(Context ctx, SharedLog log, int id) { + // MyTetheringConfiguration is used to override static method for testing. + private class MyTetheringConfiguration extends TetheringConfiguration { + MyTetheringConfiguration(Context ctx, SharedLog log, int id) { super(ctx, log, id); } @@ -327,6 +333,15 @@ public class TetheringTest { return mOffloadHardwareInterface; } + @Override + public OffloadController getOffloadController(Handler h, SharedLog log, + OffloadController.Dependencies deps) { + mOffloadCtrl = spy(super.getOffloadController(h, log, deps)); + // Return real object here instead of mock because + // testReportFailCallbackIfOffloadNotSupported depend on real OffloadController object. + return mOffloadCtrl; + } + @Override public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx, StateMachine target, SharedLog log, int what) { @@ -351,6 +366,13 @@ public class TetheringTest { return mNetworkRequest; } + @Override + public EntitlementManager getEntitlementManager(Context ctx, StateMachine target, + SharedLog log, int what) { + mEntitleMgr = spy(super.getEntitlementManager(ctx, target, log, what)); + return mEntitleMgr; + } + @Override public boolean isTetheringSupported() { return true; @@ -359,7 +381,8 @@ public class TetheringTest { @Override public TetheringConfiguration generateTetheringConfiguration(Context ctx, SharedLog log, int subId) { - return new MockTetheringConfiguration(ctx, log, subId); + mConfig = spy(new MyTetheringConfiguration(ctx, log, subId)); + return mConfig; } @Override @@ -1726,6 +1749,17 @@ public class TetheringTest { verify(mNotificationUpdater, never()).onUpstreamCapabilitiesChanged(any()); } + @Test + public void testDumpTetheringLog() throws Exception { + final FileDescriptor mockFd = mock(FileDescriptor.class); + final PrintWriter mockPw = mock(PrintWriter.class); + runUsbTethering(null); + mTethering.dump(mockFd, mockPw, new String[0]); + verify(mConfig).dump(any()); + verify(mEntitleMgr).dump(any()); + verify(mOffloadCtrl).dump(any()); + } + // TODO: Test that a request for hotspot mode doesn't interfere with an // already operating tethering mode interface. } From 1aba7987de269fed94073e7694f604c2e17750cd Mon Sep 17 00:00:00 2001 From: Jeongik Cha Date: Thu, 7 May 2020 11:26:19 +0000 Subject: [PATCH 129/188] Use stable networkstack-aidl-interfaces Test: m nothing Bug: 133526962 Original-Change: https://android-review.googlesource.com/1301313 Merged-In: I507f40866d04db5ed3361831e01eaa4dfaf20bed Change-Id: I507f40866d04db5ed3361831e01eaa4dfaf20bed --- Tethering/Android.bp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tethering/Android.bp b/Tethering/Android.bp index bfb65241ec..07a4f71117 100644 --- a/Tethering/Android.bp +++ b/Tethering/Android.bp @@ -27,7 +27,7 @@ java_defaults { "androidx.annotation_annotation", "netd_aidl_interface-V3-java", "netlink-client", - "networkstack-aidl-interfaces-unstable-java", + "networkstack-aidl-interfaces-java", "android.hardware.tetheroffload.config-V1.0-java", "android.hardware.tetheroffload.control-V1.0-java", "net-utils-framework-common", From 92afd387fde5d46b7d113be9084c61516c4446e1 Mon Sep 17 00:00:00 2001 From: Mark Chien Date: Thu, 7 May 2020 11:26:35 +0000 Subject: [PATCH 130/188] Refactor the EntitlementManager 1. Change ArraySet usage to BitSet 2. Change mCellularUpstreamPermitted to mLastCellularUpstreamPermitted. Before this change: a member variable(mCellularUpstreamPermitted) is used to check whether cellular upstream is permitted, the code must ensure to update this variable once entitlement result is changed or the entitlement check is triggered but does not have a result yet. In this change: Instead of storing the information about whether cellular is permitted in a member variable. The information is recalculated every time when user call isCellularUpstreamPermitted(). Now isCellularUpstreamPermitted() is always be used to check whether cellular upstream is permitted no matter inside or outside EntitlementManager. This make the code be easier to maintain that we do not need to care when mCellularUpstreamPermitted need to be updated because the information would be recalculated every time. And the recalculation is lock free because this is only used inside tethering while running in the same thread. Bug: 141256482 Test: atest TetheringTests Merged-In: Ic83f42ff4eec38adf039d55d80fcb9b0f16373cc Change-Id: Ic83f42ff4eec38adf039d55d80fcb9b0f16373cc --- .../tethering/EntitlementManager.java | 113 +++++++++--------- 1 file changed, 56 insertions(+), 57 deletions(-) diff --git a/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java b/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java index 049a9f68bb..d9785415c8 100644 --- a/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java +++ b/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java @@ -45,13 +45,13 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.provider.Settings; import android.telephony.CarrierConfigManager; -import android.util.ArraySet; import android.util.SparseIntArray; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.StateMachine; import java.io.PrintWriter; +import java.util.BitSet; /** * Re-check tethering provisioning for enabled downstream tether types. @@ -74,11 +74,11 @@ public class EntitlementManager { private final ComponentName mSilentProvisioningService; private static final int MS_PER_HOUR = 60 * 60 * 1000; - // The ArraySet contains enabled downstream types, ex: + // The BitSet is the bit map of each enabled downstream types, ex: // {@link TetheringManager.TETHERING_WIFI} // {@link TetheringManager.TETHERING_USB} // {@link TetheringManager.TETHERING_BLUETOOTH} - private final ArraySet mCurrentTethers; + private final BitSet mCurrentDownstreams; private final Context mContext; private final int mPermissionChangeMessageCode; private final SharedLog mLog; @@ -87,9 +87,9 @@ public class EntitlementManager { private final StateMachine mTetherMasterSM; // Key: TetheringManager.TETHERING_*(downstream). // Value: TetheringManager.TETHER_ERROR_{NO_ERROR or PROVISION_FAILED}(provisioning result). - private final SparseIntArray mCellularPermitted; + private final SparseIntArray mCurrentEntitlementResults; private PendingIntent mProvisioningRecheckAlarm; - private boolean mCellularUpstreamPermitted = true; + private boolean mLastCellularUpstreamPermitted = true; private boolean mUsingCellularAsUpstream = false; private boolean mNeedReRunProvisioningUi = false; private OnUiEntitlementFailedListener mListener; @@ -97,11 +97,10 @@ public class EntitlementManager { public EntitlementManager(Context ctx, StateMachine tetherMasterSM, SharedLog log, int permissionChangeMessageCode) { - mContext = ctx; mLog = log.forSubComponent(TAG); - mCurrentTethers = new ArraySet(); - mCellularPermitted = new SparseIntArray(); + mCurrentDownstreams = new BitSet(); + mCurrentEntitlementResults = new SparseIntArray(); mEntitlementCacheValue = new SparseIntArray(); mTetherMasterSM = tetherMasterSM; mPermissionChangeMessageCode = permissionChangeMessageCode; @@ -144,13 +143,19 @@ public class EntitlementManager { * Check if cellular upstream is permitted. */ public boolean isCellularUpstreamPermitted() { - // If provisioning is required and EntitlementManager don't know any downstream, - // cellular upstream should not be allowed. final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); - if (mCurrentTethers.size() == 0 && isTetherProvisioningRequired(config)) { - return false; - } - return mCellularUpstreamPermitted; + + return isCellularUpstreamPermitted(config); + } + + private boolean isCellularUpstreamPermitted(final TetheringConfiguration config) { + if (!isTetherProvisioningRequired(config)) return true; + + // If provisioning is required and EntitlementManager doesn't know any downstreams, + // cellular upstream should not be allowed. + if (mCurrentDownstreams.isEmpty()) return false; + + return mCurrentEntitlementResults.indexOfValue(TETHER_ERROR_NO_ERROR) > -1; } /** @@ -164,29 +169,22 @@ public class EntitlementManager { public void startProvisioningIfNeeded(int downstreamType, boolean showProvisioningUi) { if (!isValidDownstreamType(downstreamType)) return; - if (!mCurrentTethers.contains(downstreamType)) mCurrentTethers.add(downstreamType); + mCurrentDownstreams.set(downstreamType, true); final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); - if (isTetherProvisioningRequired(config)) { - // If provisioning is required and the result is not available yet, - // cellular upstream should not be allowed. - if (mCellularPermitted.size() == 0) { - mCellularUpstreamPermitted = false; - } - // If upstream is not cellular, provisioning app would not be launched - // till upstream change to cellular. - if (mUsingCellularAsUpstream) { - if (showProvisioningUi) { - runUiTetherProvisioning(downstreamType, config.activeDataSubId); - } else { - runSilentTetherProvisioning(downstreamType, config.activeDataSubId); - } - mNeedReRunProvisioningUi = false; + if (!isTetherProvisioningRequired(config)) return; + + // If upstream is not cellular, provisioning app would not be launched + // till upstream change to cellular. + if (mUsingCellularAsUpstream) { + if (showProvisioningUi) { + runUiTetherProvisioning(downstreamType, config.activeDataSubId); } else { - mNeedReRunProvisioningUi |= showProvisioningUi; + runSilentTetherProvisioning(downstreamType, config.activeDataSubId); } + mNeedReRunProvisioningUi = false; } else { - mCellularUpstreamPermitted = true; + mNeedReRunProvisioningUi |= showProvisioningUi; } } @@ -195,14 +193,14 @@ public class EntitlementManager { * * @param type tethering type from TetheringManager.TETHERING_{@code *} */ - public void stopProvisioningIfNeeded(int type) { - if (!isValidDownstreamType(type)) return; + public void stopProvisioningIfNeeded(int downstreamType) { + if (!isValidDownstreamType(downstreamType)) return; - mCurrentTethers.remove(type); + mCurrentDownstreams.set(downstreamType, false); // There are lurking bugs where the notion of "provisioning required" or // "tethering supported" may change without without tethering being notified properly. // Remove the mapping all the time no matter provisioning is required or not. - removeDownstreamMapping(type); + removeDownstreamMapping(downstreamType); } /** @@ -213,7 +211,7 @@ public class EntitlementManager { public void notifyUpstream(boolean isCellular) { if (DBG) { mLog.i("notifyUpstream: " + isCellular - + ", mCellularUpstreamPermitted: " + mCellularUpstreamPermitted + + ", mLastCellularUpstreamPermitted: " + mLastCellularUpstreamPermitted + ", mNeedReRunProvisioningUi: " + mNeedReRunProvisioningUi); } mUsingCellularAsUpstream = isCellular; @@ -231,7 +229,7 @@ public class EntitlementManager { } private void maybeRunProvisioning(final TetheringConfiguration config) { - if (mCurrentTethers.size() == 0 || !isTetherProvisioningRequired(config)) { + if (mCurrentDownstreams.isEmpty() || !isTetherProvisioningRequired(config)) { return; } @@ -239,8 +237,9 @@ public class EntitlementManager { // are allowed. Therefore even if the silent check here ends in a failure and the UI later // yields success, then the downstream that got a failure will re-evaluate as a result of // the change and get the new correct value. - for (Integer downstream : mCurrentTethers) { - if (mCellularPermitted.indexOfKey(downstream) < 0) { + for (int downstream = mCurrentDownstreams.nextSetBit(0); downstream >= 0; + downstream = mCurrentDownstreams.nextSetBit(downstream + 1)) { + if (mCurrentEntitlementResults.indexOfKey(downstream) < 0) { if (mNeedReRunProvisioningUi) { mNeedReRunProvisioningUi = false; runUiTetherProvisioning(downstream, config.activeDataSubId); @@ -286,7 +285,7 @@ public class EntitlementManager { mLog.log("reevaluateSimCardProvisioning() don't run in TetherMaster thread"); } mEntitlementCacheValue.clear(); - mCellularPermitted.clear(); + mCurrentEntitlementResults.clear(); // TODO: refine provisioning check to isTetherProvisioningRequired() ?? if (!config.hasMobileHotspotProvisionApp() @@ -410,22 +409,21 @@ public class EntitlementManager { } private void evaluateCellularPermission(final TetheringConfiguration config) { - final boolean oldPermitted = mCellularUpstreamPermitted; - mCellularUpstreamPermitted = (!isTetherProvisioningRequired(config) - || mCellularPermitted.indexOfValue(TETHER_ERROR_NO_ERROR) > -1); + final boolean oldPermitted = mLastCellularUpstreamPermitted; + mLastCellularUpstreamPermitted = isCellularUpstreamPermitted(config); if (DBG) { mLog.i("Cellular permission change from " + oldPermitted - + " to " + mCellularUpstreamPermitted); + + " to " + mLastCellularUpstreamPermitted); } - if (mCellularUpstreamPermitted != oldPermitted) { - mLog.log("Cellular permission change: " + mCellularUpstreamPermitted); + if (mLastCellularUpstreamPermitted != oldPermitted) { + mLog.log("Cellular permission change: " + mLastCellularUpstreamPermitted); mTetherMasterSM.sendMessage(mPermissionChangeMessageCode); } // Only schedule periodic re-check when tether is provisioned // and the result is ok. - if (mCellularUpstreamPermitted && mCellularPermitted.size() > 0) { + if (mLastCellularUpstreamPermitted && mCurrentEntitlementResults.size() > 0) { scheduleProvisioningRechecks(config); } else { cancelTetherProvisioningRechecks(); @@ -441,10 +439,10 @@ public class EntitlementManager { */ protected void addDownstreamMapping(int type, int resultCode) { mLog.i("addDownstreamMapping: " + type + ", result: " + resultCode - + " ,TetherTypeRequested: " + mCurrentTethers.contains(type)); - if (!mCurrentTethers.contains(type)) return; + + " ,TetherTypeRequested: " + mCurrentDownstreams.get(type)); + if (!mCurrentDownstreams.get(type)) return; - mCellularPermitted.put(type, resultCode); + mCurrentEntitlementResults.put(type, resultCode); final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); evaluateCellularPermission(config); } @@ -455,7 +453,7 @@ public class EntitlementManager { */ protected void removeDownstreamMapping(int type) { mLog.i("removeDownstreamMapping: " + type); - mCellularPermitted.delete(type); + mCurrentEntitlementResults.delete(type); final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); evaluateCellularPermission(config); } @@ -488,14 +486,15 @@ public class EntitlementManager { * @param pw {@link PrintWriter} is used to print formatted */ public void dump(PrintWriter pw) { - pw.print("mCellularUpstreamPermitted: "); - pw.println(mCellularUpstreamPermitted); - for (Integer type : mCurrentTethers) { + pw.print("isCellularUpstreamPermitted: "); + pw.println(isCellularUpstreamPermitted()); + for (int type = mCurrentDownstreams.nextSetBit(0); type >= 0; + type = mCurrentDownstreams.nextSetBit(type + 1)) { pw.print("Type: "); pw.print(typeString(type)); - if (mCellularPermitted.indexOfKey(type) > -1) { + if (mCurrentEntitlementResults.indexOfKey(type) > -1) { pw.print(", Value: "); - pw.println(errorString(mCellularPermitted.get(type))); + pw.println(errorString(mCurrentEntitlementResults.get(type))); } else { pw.println(", Value: empty"); } From 0dafa92b93f40dd31a00f73543ce6db5612f75ec Mon Sep 17 00:00:00 2001 From: Mark Chien Date: Fri, 8 May 2020 11:00:42 +0000 Subject: [PATCH 131/188] Address the comment of aosp/1288493 Bug: 141256482 Test: atest TetheringTests Merged-In: I0cf337625cee31a47879c59e9b18657ea7624eb4 Change-Id: I0cf337625cee31a47879c59e9b18657ea7624eb4 --- .../tethering/EntitlementManager.java | 60 +++++---- .../networkstack/tethering/Tethering.java | 5 +- .../tethering/TetheringDependencies.java | 6 +- .../tethering/EntitlementManagerTest.java | 116 +++++++++++------- .../networkstack/tethering/TetheringTest.java | 8 +- 5 files changed, 114 insertions(+), 81 deletions(-) diff --git a/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java b/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java index d9785415c8..23b8be1d07 100644 --- a/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java +++ b/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java @@ -37,6 +37,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.net.util.SharedLog; import android.os.Bundle; +import android.os.ConditionVariable; import android.os.Handler; import android.os.Parcel; import android.os.PersistableBundle; @@ -48,7 +49,6 @@ import android.telephony.CarrierConfigManager; import android.util.SparseIntArray; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.StateMachine; import java.io.PrintWriter; import java.util.BitSet; @@ -73,6 +73,7 @@ public class EntitlementManager { private final ComponentName mSilentProvisioningService; private static final int MS_PER_HOUR = 60 * 60 * 1000; + private static final int DUMP_TIMEOUT = 10_000; // The BitSet is the bit map of each enabled downstream types, ex: // {@link TetheringManager.TETHERING_WIFI} @@ -80,14 +81,13 @@ public class EntitlementManager { // {@link TetheringManager.TETHERING_BLUETOOTH} private final BitSet mCurrentDownstreams; private final Context mContext; - private final int mPermissionChangeMessageCode; private final SharedLog mLog; private final SparseIntArray mEntitlementCacheValue; private final Handler mHandler; - private final StateMachine mTetherMasterSM; // Key: TetheringManager.TETHERING_*(downstream). // Value: TetheringManager.TETHER_ERROR_{NO_ERROR or PROVISION_FAILED}(provisioning result). private final SparseIntArray mCurrentEntitlementResults; + private final Runnable mPermissionChangeCallback; private PendingIntent mProvisioningRecheckAlarm; private boolean mLastCellularUpstreamPermitted = true; private boolean mUsingCellularAsUpstream = false; @@ -95,16 +95,15 @@ public class EntitlementManager { private OnUiEntitlementFailedListener mListener; private TetheringConfigurationFetcher mFetcher; - public EntitlementManager(Context ctx, StateMachine tetherMasterSM, SharedLog log, - int permissionChangeMessageCode) { + public EntitlementManager(Context ctx, Handler h, SharedLog log, + Runnable callback) { mContext = ctx; mLog = log.forSubComponent(TAG); mCurrentDownstreams = new BitSet(); mCurrentEntitlementResults = new SparseIntArray(); mEntitlementCacheValue = new SparseIntArray(); - mTetherMasterSM = tetherMasterSM; - mPermissionChangeMessageCode = permissionChangeMessageCode; - mHandler = tetherMasterSM.getHandler(); + mPermissionChangeCallback = callback; + mHandler = h; mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_PROVISIONING_ALARM), null, mHandler); mSilentProvisioningService = ComponentName.unflattenFromString( @@ -409,25 +408,25 @@ public class EntitlementManager { } private void evaluateCellularPermission(final TetheringConfiguration config) { - final boolean oldPermitted = mLastCellularUpstreamPermitted; - mLastCellularUpstreamPermitted = isCellularUpstreamPermitted(config); + final boolean permitted = isCellularUpstreamPermitted(config); if (DBG) { - mLog.i("Cellular permission change from " + oldPermitted - + " to " + mLastCellularUpstreamPermitted); + mLog.i("Cellular permission change from " + mLastCellularUpstreamPermitted + + " to " + permitted); } - if (mLastCellularUpstreamPermitted != oldPermitted) { - mLog.log("Cellular permission change: " + mLastCellularUpstreamPermitted); - mTetherMasterSM.sendMessage(mPermissionChangeMessageCode); + if (mLastCellularUpstreamPermitted != permitted) { + mLog.log("Cellular permission change: " + permitted); + mPermissionChangeCallback.run(); } // Only schedule periodic re-check when tether is provisioned // and the result is ok. - if (mLastCellularUpstreamPermitted && mCurrentEntitlementResults.size() > 0) { + if (permitted && mCurrentEntitlementResults.size() > 0) { scheduleProvisioningRechecks(config); } else { cancelTetherProvisioningRechecks(); } + mLastCellularUpstreamPermitted = permitted; } /** @@ -486,18 +485,25 @@ public class EntitlementManager { * @param pw {@link PrintWriter} is used to print formatted */ public void dump(PrintWriter pw) { - pw.print("isCellularUpstreamPermitted: "); - pw.println(isCellularUpstreamPermitted()); - for (int type = mCurrentDownstreams.nextSetBit(0); type >= 0; - type = mCurrentDownstreams.nextSetBit(type + 1)) { - pw.print("Type: "); - pw.print(typeString(type)); - if (mCurrentEntitlementResults.indexOfKey(type) > -1) { - pw.print(", Value: "); - pw.println(errorString(mCurrentEntitlementResults.get(type))); - } else { - pw.println(", Value: empty"); + final ConditionVariable mWaiting = new ConditionVariable(); + mHandler.post(() -> { + pw.print("isCellularUpstreamPermitted: "); + pw.println(isCellularUpstreamPermitted()); + for (int type = mCurrentDownstreams.nextSetBit(0); type >= 0; + type = mCurrentDownstreams.nextSetBit(type + 1)) { + pw.print("Type: "); + pw.print(typeString(type)); + if (mCurrentEntitlementResults.indexOfKey(type) > -1) { + pw.print(", Value: "); + pw.println(errorString(mCurrentEntitlementResults.get(type))); + } else { + pw.println(", Value: empty"); + } } + mWaiting.open(); + }); + if (!mWaiting.block(DUMP_TIMEOUT)) { + pw.println("... dump timed out after " + DUMP_TIMEOUT + "ms"); } } diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java index b2a43c47d1..753abc9f69 100644 --- a/Tethering/src/com/android/networkstack/tethering/Tethering.java +++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java @@ -284,8 +284,9 @@ public class Tethering { filter.addAction(ACTION_CARRIER_CONFIG_CHANGED); // EntitlementManager will send EVENT_UPSTREAM_PERMISSION_CHANGED when cellular upstream // permission is changed according to entitlement check result. - mEntitlementMgr = mDeps.getEntitlementManager(mContext, mTetherMasterSM, mLog, - TetherMasterSM.EVENT_UPSTREAM_PERMISSION_CHANGED); + mEntitlementMgr = mDeps.getEntitlementManager(mContext, mHandler, mLog, + () -> mTetherMasterSM.sendMessage( + TetherMasterSM.EVENT_UPSTREAM_PERMISSION_CHANGED)); mEntitlementMgr.setOnUiEntitlementFailedListener((int downstream) -> { mLog.log("OBSERVED UiEnitlementFailed"); stopTethering(downstream); diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java b/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java index 802f2acb33..ce546c701a 100644 --- a/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java @@ -96,9 +96,9 @@ public abstract class TetheringDependencies { /** * Get a reference to the EntitlementManager to be used by tethering. */ - public EntitlementManager getEntitlementManager(Context ctx, StateMachine target, - SharedLog log, int what) { - return new EntitlementManager(ctx, target, log, what); + public EntitlementManager getEntitlementManager(Context ctx, Handler h, SharedLog log, + Runnable callback) { + return new EntitlementManager(ctx, h, log, callback); } /** diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java index 8bd0edc249..a692935375 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java @@ -37,6 +37,8 @@ import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -45,7 +47,7 @@ import android.content.Context; import android.content.res.Resources; import android.net.util.SharedLog; import android.os.Bundle; -import android.os.Message; +import android.os.Handler; import android.os.PersistableBundle; import android.os.ResultReceiver; import android.os.SystemProperties; @@ -56,26 +58,22 @@ import android.telephony.CarrierConfigManager; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import com.android.internal.util.State; -import com.android.internal.util.StateMachine; import com.android.internal.util.test.BroadcastInterceptingContext; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.MockitoSession; import org.mockito.quality.Strictness; -import java.util.ArrayList; - @RunWith(AndroidJUnit4.class) @SmallTest public final class EntitlementManagerTest { - private static final int EVENT_EM_UPDATE = 1; private static final String[] PROVISIONING_APP_NAME = {"some", "app"}; private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app"; @@ -90,8 +88,8 @@ public final class EntitlementManagerTest { private final PersistableBundle mCarrierConfig = new PersistableBundle(); private final TestLooper mLooper = new TestLooper(); private Context mMockContext; + private Runnable mPermissionChangeCallback; - private TestStateMachine mSM; private WrappedEntitlementManager mEnMgr; private TetheringConfiguration mConfig; private MockitoSession mMockingSession; @@ -112,9 +110,9 @@ public final class EntitlementManagerTest { public int uiProvisionCount = 0; public int silentProvisionCount = 0; - public WrappedEntitlementManager(Context ctx, StateMachine target, - SharedLog log, int what) { - super(ctx, target, log, what); + public WrappedEntitlementManager(Context ctx, Handler h, SharedLog log, + Runnable callback) { + super(ctx, h, log, callback); } public void reset() { @@ -169,8 +167,9 @@ public final class EntitlementManagerTest { when(mLog.forSubComponent(anyString())).thenReturn(mLog); mMockContext = new MockContext(mContext); - mSM = new TestStateMachine(); - mEnMgr = new WrappedEntitlementManager(mMockContext, mSM, mLog, EVENT_EM_UPDATE); + mPermissionChangeCallback = spy(() -> { }); + mEnMgr = new WrappedEntitlementManager(mMockContext, new Handler(mLooper.getLooper()), mLog, + mPermissionChangeCallback); mEnMgr.setOnUiEntitlementFailedListener(mEntitlementFailedListener); mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); mEnMgr.setTetheringConfigurationFetcher(() -> { @@ -180,10 +179,6 @@ public final class EntitlementManagerTest { @After public void tearDown() throws Exception { - if (mSM != null) { - mSM.quit(); - mSM = null; - } mMockingSession.finishMocking(); } @@ -350,68 +345,105 @@ public final class EntitlementManagerTest { mEnMgr.reset(); } + private void assertPermissionChangeCallback(InOrder inOrder) { + inOrder.verify(mPermissionChangeCallback, times(1)).run(); + } + + private void assertNoPermissionChange(InOrder inOrder) { + inOrder.verifyNoMoreInteractions(); + } + @Test public void verifyPermissionResult() { + final InOrder inOrder = inOrder(mPermissionChangeCallback); setupForRequiredProvisioning(); mEnMgr.notifyUpstream(true); mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED; mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true); mLooper.dispatchAll(); + // Permitted: true -> false + assertPermissionChangeCallback(inOrder); assertFalse(mEnMgr.isCellularUpstreamPermitted()); + mEnMgr.stopProvisioningIfNeeded(TETHERING_WIFI); mLooper.dispatchAll(); + // Permitted: false -> false + assertNoPermissionChange(inOrder); + mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true); mLooper.dispatchAll(); + // Permitted: false -> true + assertPermissionChangeCallback(inOrder); assertTrue(mEnMgr.isCellularUpstreamPermitted()); } @Test public void verifyPermissionIfAllNotApproved() { + final InOrder inOrder = inOrder(mPermissionChangeCallback); setupForRequiredProvisioning(); mEnMgr.notifyUpstream(true); mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED; mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true); mLooper.dispatchAll(); + // Permitted: true -> false + assertPermissionChangeCallback(inOrder); assertFalse(mEnMgr.isCellularUpstreamPermitted()); + mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED; mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true); mLooper.dispatchAll(); + // Permitted: false -> false + assertNoPermissionChange(inOrder); assertFalse(mEnMgr.isCellularUpstreamPermitted()); + mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED; mEnMgr.startProvisioningIfNeeded(TETHERING_BLUETOOTH, true); mLooper.dispatchAll(); + // Permitted: false -> false + assertNoPermissionChange(inOrder); assertFalse(mEnMgr.isCellularUpstreamPermitted()); } @Test public void verifyPermissionIfAnyApproved() { + final InOrder inOrder = inOrder(mPermissionChangeCallback); setupForRequiredProvisioning(); mEnMgr.notifyUpstream(true); mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true); mLooper.dispatchAll(); + // Permitted: true -> true + assertNoPermissionChange(inOrder); assertTrue(mEnMgr.isCellularUpstreamPermitted()); - mLooper.dispatchAll(); + mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED; mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true); mLooper.dispatchAll(); + // Permitted: true -> true + assertNoPermissionChange(inOrder); assertTrue(mEnMgr.isCellularUpstreamPermitted()); + mEnMgr.stopProvisioningIfNeeded(TETHERING_WIFI); mLooper.dispatchAll(); + // Permitted: true -> false + assertPermissionChangeCallback(inOrder); assertFalse(mEnMgr.isCellularUpstreamPermitted()); - } @Test public void verifyPermissionWhenProvisioningNotStarted() { + final InOrder inOrder = inOrder(mPermissionChangeCallback); assertTrue(mEnMgr.isCellularUpstreamPermitted()); + assertNoPermissionChange(inOrder); setupForRequiredProvisioning(); assertFalse(mEnMgr.isCellularUpstreamPermitted()); + assertNoPermissionChange(inOrder); } @Test public void testRunTetherProvisioning() { + final InOrder inOrder = inOrder(mPermissionChangeCallback); setupForRequiredProvisioning(); // 1. start ui provisioning, upstream is mobile mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; @@ -421,16 +453,22 @@ public final class EntitlementManagerTest { mLooper.dispatchAll(); assertEquals(1, mEnMgr.uiProvisionCount); assertEquals(0, mEnMgr.silentProvisionCount); + // Permitted: true -> true + assertNoPermissionChange(inOrder); assertTrue(mEnMgr.isCellularUpstreamPermitted()); mEnMgr.reset(); + // 2. start no-ui provisioning mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, false); mLooper.dispatchAll(); assertEquals(0, mEnMgr.uiProvisionCount); assertEquals(1, mEnMgr.silentProvisionCount); + // Permitted: true -> true + assertNoPermissionChange(inOrder); assertTrue(mEnMgr.isCellularUpstreamPermitted()); mEnMgr.reset(); + // 3. tear down mobile, then start ui provisioning mEnMgr.notifyUpstream(false); mLooper.dispatchAll(); @@ -438,44 +476,58 @@ public final class EntitlementManagerTest { mLooper.dispatchAll(); assertEquals(0, mEnMgr.uiProvisionCount); assertEquals(0, mEnMgr.silentProvisionCount); + assertNoPermissionChange(inOrder); mEnMgr.reset(); + // 4. switch upstream back to mobile mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; mEnMgr.notifyUpstream(true); mLooper.dispatchAll(); assertEquals(1, mEnMgr.uiProvisionCount); assertEquals(0, mEnMgr.silentProvisionCount); + // Permitted: true -> true + assertNoPermissionChange(inOrder); assertTrue(mEnMgr.isCellularUpstreamPermitted()); mEnMgr.reset(); + // 5. tear down mobile, then switch SIM mEnMgr.notifyUpstream(false); mLooper.dispatchAll(); mEnMgr.reevaluateSimCardProvisioning(mConfig); assertEquals(0, mEnMgr.uiProvisionCount); assertEquals(0, mEnMgr.silentProvisionCount); + assertNoPermissionChange(inOrder); mEnMgr.reset(); + // 6. switch upstream back to mobile again mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED; mEnMgr.notifyUpstream(true); mLooper.dispatchAll(); assertEquals(0, mEnMgr.uiProvisionCount); assertEquals(3, mEnMgr.silentProvisionCount); + // Permitted: true -> false + assertPermissionChangeCallback(inOrder); assertFalse(mEnMgr.isCellularUpstreamPermitted()); mEnMgr.reset(); + // 7. start ui provisioning, upstream is mobile, downstream is ethernet mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; mEnMgr.startProvisioningIfNeeded(TETHERING_ETHERNET, true); mLooper.dispatchAll(); assertEquals(1, mEnMgr.uiProvisionCount); assertEquals(0, mEnMgr.silentProvisionCount); + // Permitted: false -> true + assertPermissionChangeCallback(inOrder); assertTrue(mEnMgr.isCellularUpstreamPermitted()); mEnMgr.reset(); + // 8. downstream is invalid mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI_P2P, true); mLooper.dispatchAll(); assertEquals(0, mEnMgr.uiProvisionCount); assertEquals(0, mEnMgr.silentProvisionCount); + assertNoPermissionChange(inOrder); mEnMgr.reset(); } @@ -491,32 +543,4 @@ public final class EntitlementManagerTest { assertEquals(1, mEnMgr.uiProvisionCount); verify(mEntitlementFailedListener, times(1)).onUiEntitlementFailed(TETHERING_WIFI); } - - public class TestStateMachine extends StateMachine { - public final ArrayList messages = new ArrayList<>(); - private final State - mLoggingState = new EntitlementManagerTest.TestStateMachine.LoggingState(); - - class LoggingState extends State { - @Override public void enter() { - messages.clear(); - } - - @Override public void exit() { - messages.clear(); - } - - @Override public boolean processMessage(Message msg) { - messages.add(msg); - return false; - } - } - - public TestStateMachine() { - super("EntitlementManagerTest.TestStateMachine", mLooper.getLooper()); - addState(mLoggingState); - setInitialState(mLoggingState); - super.start(); - } - } } 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 fff7a70f54..fa260a4893 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java @@ -367,9 +367,9 @@ public class TetheringTest { } @Override - public EntitlementManager getEntitlementManager(Context ctx, StateMachine target, - SharedLog log, int what) { - mEntitleMgr = spy(super.getEntitlementManager(ctx, target, log, what)); + public EntitlementManager getEntitlementManager(Context ctx, Handler h, SharedLog log, + Runnable callback) { + mEntitleMgr = spy(super.getEntitlementManager(ctx, h, log, callback)); return mEntitleMgr; } @@ -1754,10 +1754,12 @@ public class TetheringTest { final FileDescriptor mockFd = mock(FileDescriptor.class); final PrintWriter mockPw = mock(PrintWriter.class); runUsbTethering(null); + mLooper.startAutoDispatch(); mTethering.dump(mockFd, mockPw, new String[0]); verify(mConfig).dump(any()); verify(mEntitleMgr).dump(any()); verify(mOffloadCtrl).dump(any()); + mLooper.stopAutoDispatch(); } // TODO: Test that a request for hotspot mode doesn't interfere with an From dc3b82db705fe098c2b73ae25355efd36b087f7f Mon Sep 17 00:00:00 2001 From: markchien Date: Fri, 8 May 2020 18:55:26 +0800 Subject: [PATCH 132/188] Allow to exempt from entitlement check To exempt from entitlement check, caller need to hold TETHER_PRIVILEGED permission. Bug: 141256482 Test: atest TetheringTests Change-Id: I2eb37f5e92f5f5150a7fb7c25b945e28704d27a0 Merged-In: I2eb37f5e92f5f5150a7fb7c25b945e28704d27a0 --- .../tethering/EntitlementManager.java | 34 +++++++- .../networkstack/tethering/Tethering.java | 8 +- .../tethering/TetheringService.java | 80 +++++++++---------- .../tethering/EntitlementManagerTest.java | 29 +++++++ .../networkstack/tethering/TetheringTest.java | 80 +++++++++++++++++-- 5 files changed, 179 insertions(+), 52 deletions(-) diff --git a/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java b/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java index 23b8be1d07..3c6e8d88ed 100644 --- a/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java +++ b/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java @@ -80,6 +80,7 @@ public class EntitlementManager { // {@link TetheringManager.TETHERING_USB} // {@link TetheringManager.TETHERING_BLUETOOTH} private final BitSet mCurrentDownstreams; + private final BitSet mExemptedDownstreams; private final Context mContext; private final SharedLog mLog; private final SparseIntArray mEntitlementCacheValue; @@ -100,6 +101,7 @@ public class EntitlementManager { mContext = ctx; mLog = log.forSubComponent(TAG); mCurrentDownstreams = new BitSet(); + mExemptedDownstreams = new BitSet(); mCurrentEntitlementResults = new SparseIntArray(); mEntitlementCacheValue = new SparseIntArray(); mPermissionChangeCallback = callback; @@ -150,13 +152,29 @@ public class EntitlementManager { private boolean isCellularUpstreamPermitted(final TetheringConfiguration config) { if (!isTetherProvisioningRequired(config)) return true; - // If provisioning is required and EntitlementManager doesn't know any downstreams, - // cellular upstream should not be allowed. - if (mCurrentDownstreams.isEmpty()) return false; + // If provisioning is required and EntitlementManager doesn't know any downstreams, cellular + // upstream should not be enabled. Enable cellular upstream for exempted downstreams only + // when there is no non-exempted downstream. + if (mCurrentDownstreams.isEmpty()) return !mExemptedDownstreams.isEmpty(); return mCurrentEntitlementResults.indexOfValue(TETHER_ERROR_NO_ERROR) > -1; } + /** + * Set exempted downstream type. If there is only exempted downstream type active, + * corresponding entitlement check will not be run and cellular upstream will be permitted + * by default. If a privileged app enables tethering without a provisioning check, and then + * another app enables tethering of the same type but does not disable the provisioning check, + * then the downstream immediately loses exempt status and a provisioning check is run. + * If any non-exempted downstream type is active, the cellular upstream will be gated by the + * result of entitlement check from non-exempted downstreams. If entitlement check is still + * in progress on non-exempt downstreams, ceullar upstream would default be disabled. When any + * non-exempted downstream gets positive entitlement result, ceullar upstream will be enabled. + */ + public void setExemptedDownstreamType(final int type) { + mExemptedDownstreams.set(type, true); + } + /** * This is called when tethering starts. * Launch provisioning app if upstream is cellular. @@ -170,6 +188,8 @@ public class EntitlementManager { mCurrentDownstreams.set(downstreamType, true); + mExemptedDownstreams.set(downstreamType, false); + final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); if (!isTetherProvisioningRequired(config)) return; @@ -200,6 +220,7 @@ public class EntitlementManager { // "tethering supported" may change without without tethering being notified properly. // Remove the mapping all the time no matter provisioning is required or not. removeDownstreamMapping(downstreamType); + mExemptedDownstreams.set(downstreamType, false); } /** @@ -505,6 +526,13 @@ public class EntitlementManager { if (!mWaiting.block(DUMP_TIMEOUT)) { pw.println("... dump timed out after " + DUMP_TIMEOUT + "ms"); } + pw.print("Exempted: ["); + for (int type = mExemptedDownstreams.nextSetBit(0); type >= 0; + type = mExemptedDownstreams.nextSetBit(type + 1)) { + pw.print(typeString(type)); + pw.print(", "); + } + pw.println("]"); } private static String typeString(int type) { diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java index 753abc9f69..ae6119f24f 100644 --- a/Tethering/src/com/android/networkstack/tethering/Tethering.java +++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java @@ -516,8 +516,12 @@ public class Tethering { } mActiveTetheringRequests.put(request.tetheringType, request); - mEntitlementMgr.startProvisioningIfNeeded(request.tetheringType, - request.showProvisioningUi); + if (request.exemptFromEntitlementCheck) { + mEntitlementMgr.setExemptedDownstreamType(request.tetheringType); + } else { + mEntitlementMgr.startProvisioningIfNeeded(request.tetheringType, + request.showProvisioningUi); + } enableTetheringInternal(request.tetheringType, true /* enabled */, listener); }); } diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringService.java b/Tethering/src/com/android/networkstack/tethering/TetheringService.java index 3ed211520d..d07c555f66 100644 --- a/Tethering/src/com/android/networkstack/tethering/TetheringService.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringService.java @@ -16,6 +16,8 @@ package com.android.networkstack.tethering; +import static android.Manifest.permission.ACCESS_NETWORK_STATE; +import static android.Manifest.permission.TETHER_PRIVILEGED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.net.TetheringManager.TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION; import static android.net.TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION; @@ -148,7 +150,11 @@ public class TetheringService extends Service { @Override public void startTethering(TetheringRequestParcel request, String callerPkg, IIntResultListener listener) { - if (checkAndNotifyCommonError(callerPkg, listener)) return; + if (checkAndNotifyCommonError(callerPkg, + request.exemptFromEntitlementCheck /* onlyAllowPrivileged */, + listener)) { + return; + } mTethering.startTethering(request, listener); } @@ -175,7 +181,7 @@ public class TetheringService extends Service { public void registerTetheringEventCallback(ITetheringEventCallback callback, String callerPkg) { try { - if (!mService.hasTetherAccessPermission()) { + if (!hasTetherAccessPermission()) { callback.onCallbackStopped(TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION); return; } @@ -187,7 +193,7 @@ public class TetheringService extends Service { public void unregisterTetheringEventCallback(ITetheringEventCallback callback, String callerPkg) { try { - if (!mService.hasTetherAccessPermission()) { + if (!hasTetherAccessPermission()) { callback.onCallbackStopped(TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION); return; } @@ -221,8 +227,13 @@ public class TetheringService extends Service { } private boolean checkAndNotifyCommonError(String callerPkg, IIntResultListener listener) { + return checkAndNotifyCommonError(callerPkg, false /* onlyAllowPrivileged */, listener); + } + + private boolean checkAndNotifyCommonError(final String callerPkg, + final boolean onlyAllowPrivileged, final IIntResultListener listener) { try { - if (!mService.hasTetherChangePermission(callerPkg)) { + if (!hasTetherChangePermission(callerPkg, onlyAllowPrivileged)) { listener.onResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); return true; } @@ -238,7 +249,7 @@ public class TetheringService extends Service { } private boolean checkAndNotifyCommonError(String callerPkg, ResultReceiver receiver) { - if (!mService.hasTetherChangePermission(callerPkg)) { + if (!hasTetherChangePermission(callerPkg, false /* onlyAllowPrivileged */)) { receiver.send(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION, null); return true; } @@ -250,6 +261,30 @@ public class TetheringService extends Service { return false; } + private boolean hasTetherPrivilegedPermission() { + return mService.checkCallingOrSelfPermission(TETHER_PRIVILEGED) == PERMISSION_GRANTED; + } + + private boolean hasTetherChangePermission(final String callerPkg, + final boolean onlyAllowPrivileged) { + if (hasTetherPrivilegedPermission()) return true; + + if (onlyAllowPrivileged || mTethering.isTetherProvisioningRequired()) return false; + + int uid = Binder.getCallingUid(); + // If callerPkg's uid is not same as Binder.getCallingUid(), + // checkAndNoteWriteSettingsOperation will return false and the operation will be + // denied. + return Settings.checkAndNoteWriteSettingsOperation(mService, uid, callerPkg, + false /* throwException */); + } + + private boolean hasTetherAccessPermission() { + if (hasTetherPrivilegedPermission()) return true; + + return mService.checkCallingOrSelfPermission( + ACCESS_NETWORK_STATE) == PERMISSION_GRANTED; + } } // if ro.tether.denied = true we default to no tethering @@ -266,41 +301,6 @@ public class TetheringService extends Service { return tetherEnabledInSettings && mTethering.hasTetherableConfiguration(); } - private boolean hasTetherChangePermission(String callerPkg) { - if (checkCallingOrSelfPermission( - android.Manifest.permission.TETHER_PRIVILEGED) == PERMISSION_GRANTED) { - return true; - } - - if (mTethering.isTetherProvisioningRequired()) return false; - - - int uid = Binder.getCallingUid(); - // If callerPkg's uid is not same as Binder.getCallingUid(), - // checkAndNoteWriteSettingsOperation will return false and the operation will be denied. - if (Settings.checkAndNoteWriteSettingsOperation(mContext, uid, callerPkg, - false /* throwException */)) { - return true; - } - - return false; - } - - private boolean hasTetherAccessPermission() { - if (checkCallingOrSelfPermission( - android.Manifest.permission.TETHER_PRIVILEGED) == PERMISSION_GRANTED) { - return true; - } - - if (checkCallingOrSelfPermission( - android.Manifest.permission.ACCESS_NETWORK_STATE) == PERMISSION_GRANTED) { - return true; - } - - return false; - } - - /** * An injection method for testing. */ diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java index a692935375..cdd0e243e3 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java @@ -543,4 +543,33 @@ public final class EntitlementManagerTest { assertEquals(1, mEnMgr.uiProvisionCount); verify(mEntitlementFailedListener, times(1)).onUiEntitlementFailed(TETHERING_WIFI); } + + @Test + public void testsetExemptedDownstreamType() throws Exception { + setupForRequiredProvisioning(); + // Cellular upstream is not permitted when no entitlement result. + assertFalse(mEnMgr.isCellularUpstreamPermitted()); + + // If there is exempted downstream and no other non-exempted downstreams, cellular is + // permitted. + mEnMgr.setExemptedDownstreamType(TETHERING_WIFI); + assertTrue(mEnMgr.isCellularUpstreamPermitted()); + + // If second downstream run entitlement check fail, cellular upstream is not permitted. + mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED; + mEnMgr.notifyUpstream(true); + mLooper.dispatchAll(); + mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true); + mLooper.dispatchAll(); + assertFalse(mEnMgr.isCellularUpstreamPermitted()); + + // When second downstream is down, exempted downstream can use cellular upstream. + assertEquals(1, mEnMgr.uiProvisionCount); + verify(mEntitlementFailedListener).onUiEntitlementFailed(TETHERING_USB); + mEnMgr.stopProvisioningIfNeeded(TETHERING_USB); + assertTrue(mEnMgr.isCellularUpstreamPermitted()); + + mEnMgr.stopProvisioningIfNeeded(TETHERING_WIFI); + assertFalse(mEnMgr.isCellularUpstreamPermitted()); + } } 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 fa260a4893..2bd8ae0288 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java @@ -57,6 +57,7 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.notNull; +import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.any; @@ -172,6 +173,8 @@ public class TetheringTest { private static final String TEST_P2P_IFNAME = "test_p2p-p2p0-0"; private static final String TEST_NCM_IFNAME = "test_ncm0"; private static final String TETHERING_NAME = "Tethering"; + private static final String[] PROVISIONING_APP_NAME = {"some", "app"}; + private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app"; private static final int DHCPSERVER_START_TIMEOUT_MS = 1000; @@ -539,16 +542,16 @@ public class TetheringTest { } private TetheringRequestParcel createTetheringRequestParcel(final int type) { - return createTetheringRequestParcel(type, null, null); + return createTetheringRequestParcel(type, null, null, false); } private TetheringRequestParcel createTetheringRequestParcel(final int type, - final LinkAddress serverAddr, final LinkAddress clientAddr) { + final LinkAddress serverAddr, final LinkAddress clientAddr, final boolean exempt) { final TetheringRequestParcel request = new TetheringRequestParcel(); request.tetheringType = type; request.localIPv4Address = serverAddr; request.staticClientAddress = clientAddr; - request.exemptFromEntitlementCheck = false; + request.exemptFromEntitlementCheck = exempt; request.showProvisioningUi = false; return request; @@ -1659,7 +1662,7 @@ public class TetheringTest { // Enable USB tethering and check that Tethering starts USB. mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB, - null, null), firstResult); + null, null, false), firstResult); mLooper.dispatchAll(); firstResult.assertHasResult(); verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS); @@ -1667,7 +1670,7 @@ public class TetheringTest { // Enable USB tethering again with the same request and expect no change to USB. mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB, - null, null), secondResult); + null, null, false), secondResult); mLooper.dispatchAll(); secondResult.assertHasResult(); verify(mUsbManager, never()).setCurrentFunctions(UsbManager.FUNCTION_NONE); @@ -1676,7 +1679,7 @@ public class TetheringTest { // Enable USB tethering with a different request and expect that USB is stopped and // started. mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB, - serverLinkAddr, clientLinkAddr), thirdResult); + serverLinkAddr, clientLinkAddr, false), thirdResult); mLooper.dispatchAll(); thirdResult.assertHasResult(); verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NONE); @@ -1700,7 +1703,7 @@ public class TetheringTest { final ArgumentCaptor dhcpParamsCaptor = ArgumentCaptor.forClass(DhcpServingParamsParcel.class); mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB, - serverLinkAddr, clientLinkAddr), null); + serverLinkAddr, clientLinkAddr, false), null); mLooper.dispatchAll(); verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS); mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true); @@ -1762,6 +1765,69 @@ public class TetheringTest { mLooper.stopAutoDispatch(); } + @Test + public void testExemptFromEntitlementCheck() throws Exception { + setupForRequiredProvisioning(); + final TetheringRequestParcel wifiNotExemptRequest = + createTetheringRequestParcel(TETHERING_WIFI, null, null, false); + mTethering.startTethering(wifiNotExemptRequest, null); + mLooper.dispatchAll(); + verify(mEntitleMgr).startProvisioningIfNeeded(TETHERING_WIFI, false); + verify(mEntitleMgr, never()).setExemptedDownstreamType(TETHERING_WIFI); + assertFalse(mEntitleMgr.isCellularUpstreamPermitted()); + mTethering.stopTethering(TETHERING_WIFI); + mLooper.dispatchAll(); + verify(mEntitleMgr).stopProvisioningIfNeeded(TETHERING_WIFI); + reset(mEntitleMgr); + + setupForRequiredProvisioning(); + final TetheringRequestParcel wifiExemptRequest = + createTetheringRequestParcel(TETHERING_WIFI, null, null, true); + mTethering.startTethering(wifiExemptRequest, null); + mLooper.dispatchAll(); + verify(mEntitleMgr, never()).startProvisioningIfNeeded(TETHERING_WIFI, false); + verify(mEntitleMgr).setExemptedDownstreamType(TETHERING_WIFI); + assertTrue(mEntitleMgr.isCellularUpstreamPermitted()); + mTethering.stopTethering(TETHERING_WIFI); + mLooper.dispatchAll(); + verify(mEntitleMgr).stopProvisioningIfNeeded(TETHERING_WIFI); + reset(mEntitleMgr); + + // If one app enables tethering without provisioning check first, then another app enables + // tethering of the same type but does not disable the provisioning check. + setupForRequiredProvisioning(); + mTethering.startTethering(wifiExemptRequest, null); + mLooper.dispatchAll(); + verify(mEntitleMgr, never()).startProvisioningIfNeeded(TETHERING_WIFI, false); + verify(mEntitleMgr).setExemptedDownstreamType(TETHERING_WIFI); + assertTrue(mEntitleMgr.isCellularUpstreamPermitted()); + reset(mEntitleMgr); + setupForRequiredProvisioning(); + mTethering.startTethering(wifiNotExemptRequest, null); + mLooper.dispatchAll(); + verify(mEntitleMgr).startProvisioningIfNeeded(TETHERING_WIFI, false); + verify(mEntitleMgr, never()).setExemptedDownstreamType(TETHERING_WIFI); + assertFalse(mEntitleMgr.isCellularUpstreamPermitted()); + mTethering.stopTethering(TETHERING_WIFI); + mLooper.dispatchAll(); + verify(mEntitleMgr).stopProvisioningIfNeeded(TETHERING_WIFI); + reset(mEntitleMgr); + } + + private void setupForRequiredProvisioning() { + // Produce some acceptable looking provision app setting if requested. + when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app)) + .thenReturn(PROVISIONING_APP_NAME); + when(mResources.getString(R.string.config_mobile_hotspot_provision_app_no_ui)) + .thenReturn(PROVISIONING_NO_UI_APP_NAME); + // Act like the CarrierConfigManager is present and ready unless told otherwise. + when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE)) + .thenReturn(mCarrierConfigManager); + when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(mCarrierConfig); + mCarrierConfig.putBoolean(CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, true); + mCarrierConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true); + sendConfigurationChanged(); + } // TODO: Test that a request for hotspot mode doesn't interfere with an // already operating tethering mode interface. } From 5f9f31c18b8989ef60a518f3470aadaaf82af286 Mon Sep 17 00:00:00 2001 From: Jooyung Han Date: Wed, 29 Apr 2020 02:43:30 +0900 Subject: [PATCH 133/188] Set min_sdk_version for updatable mainline modules Modules contributing mainline modules (APK/APEX) should set min_sdk_version as well as apex_available. For now setting min_sdk_version doesn't change build outputs. But build-time checks will be added soon. Bug: 145796956 Bug: 150999716 Test: m Merged-In: Ifaecb49a47a1f43edea3ea06e1cf704a177d1044 Change-Id: Ifaecb49a47a1f43edea3ea06e1cf704a177d1044 (cherry picked from commit 33aa294e96f13906f596e427b96652fe80cf199b) --- Tethering/Android.bp | 6 ++++++ Tethering/apex/Android.bp | 2 +- Tethering/common/TetheringLib/Android.bp | 6 ++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Tethering/Android.bp b/Tethering/Android.bp index cbc5e14139..735dd17933 100644 --- a/Tethering/Android.bp +++ b/Tethering/Android.bp @@ -51,6 +51,11 @@ android_library { cc_library { name: "libtetherutilsjni", sdk_version: "current", + apex_available: [ + "//apex_available:platform", // Used by InProcessTethering + "com.android.tethering", + ], + min_sdk_version: "current", srcs: [ "jni/android_net_util_TetheringUtils.cpp", ], @@ -123,4 +128,5 @@ android_app { // The permission configuration *must* be included to ensure security of the device required: ["NetworkPermissionConfig"], apex_available: ["com.android.tethering"], + min_sdk_version: "current", } diff --git a/Tethering/apex/Android.bp b/Tethering/apex/Android.bp index 20ccd2ad64..67097a79e5 100644 --- a/Tethering/apex/Android.bp +++ b/Tethering/apex/Android.bp @@ -17,7 +17,7 @@ apex { name: "com.android.tethering", updatable: true, - min_sdk_version: "R", + min_sdk_version: "current", java_libs: ["framework-tethering"], apps: ["Tethering"], manifest: "manifest.json", diff --git a/Tethering/common/TetheringLib/Android.bp b/Tethering/common/TetheringLib/Android.bp index d029d2bded..d03115a158 100644 --- a/Tethering/common/TetheringLib/Android.bp +++ b/Tethering/common/TetheringLib/Android.bp @@ -37,6 +37,12 @@ aidl_interface { cpp: { enabled: false, }, + java: { + apex_available: [ + "//apex_available:platform", + "com.android.tethering", + ], + }, }, } From 595adc05e839c9855298138e979210eef74cca69 Mon Sep 17 00:00:00 2001 From: Jooyung Han Date: Mon, 11 May 2020 13:18:29 +0900 Subject: [PATCH 134/188] InProcessTethering: set min_sdk_version Modules contributing mainline modules (APK/APEX) should set min_sdk_version as well as apex_available. InProcessTethering is a part of com.android.tethering.inprocess, therefore min_sdk_version is set. Exempt-From-Owner-Approval: CP from AOSP Bug: 145796956 Bug: 150999716 Test: m Merged-In: I655211e0b9aa4bd7cb3718b6f567f0aa1a0176a5 Change-Id: I655211e0b9aa4bd7cb3718b6f567f0aa1a0176a5 (cherry picked from commit ace605ef568fecfe148d2785f4681c9c858487aa) --- Tethering/Android.bp | 1 + 1 file changed, 1 insertion(+) diff --git a/Tethering/Android.bp b/Tethering/Android.bp index 735dd17933..1ee017be50 100644 --- a/Tethering/Android.bp +++ b/Tethering/Android.bp @@ -115,6 +115,7 @@ android_app { // InProcessTethering is a replacement for Tethering overrides: ["Tethering"], apex_available: ["com.android.tethering"], + min_sdk_version: "current", } // Updatable tethering packaged as an application From 0bbfe12d922702d8a44eac835c452d07a6ba2406 Mon Sep 17 00:00:00 2001 From: Nucca Chen Date: Tue, 12 May 2020 11:34:28 +0000 Subject: [PATCH 135/188] Add tether BPF offload config to device config and resource The tether bpf offload can be enabled by resource config and device config. The device config has higher priority and it could override this config which is set by resource config. Bug: 149997301 Test: -build, flash, boot -atest TetheringConfigurationTest Original-Change: https://android-review.googlesource.com/1276007 Use device option to control BPF offload features If BPF offload device config is not enabled: - Does not add/remove offload forwarding rules through disabling IP neighbor monitor. - Does not apply the RA MTU reduction. Bug: 149997301 Test: atest IpServerTest Original-Change: https://android-review.googlesource.com/1284578 Merged-In: I2d6f80f0229f580c4b16243a064e889a6c37f77a Change-Id: I2d6f80f0229f580c4b16243a064e889a6c37f77a --- Tethering/res/values/config.xml | 6 ++ Tethering/res/values/overlayable.xml | 5 ++ Tethering/src/android/net/ip/IpServer.java | 34 +++++++-- .../networkstack/tethering/Tethering.java | 2 +- .../tethering/TetheringConfiguration.java | 42 +++++++++-- .../unit/src/android/net/ip/IpServerTest.java | 71 ++++++++++++++++--- .../tethering/TetheringConfigurationTest.java | 46 ++++++++++++ 7 files changed, 185 insertions(+), 21 deletions(-) diff --git a/Tethering/res/values/config.xml b/Tethering/res/values/config.xml index 9dda7166a2..6604db641c 100644 --- a/Tethering/res/values/config.xml +++ b/Tethering/res/values/config.xml @@ -57,6 +57,12 @@ "bt-pan" + + true + false diff --git a/Tethering/res/values/overlayable.xml b/Tethering/res/values/overlayable.xml index 4c78a74d53..288dd5ddf3 100644 --- a/Tethering/res/values/overlayable.xml +++ b/Tethering/res/values/overlayable.xml @@ -23,6 +23,11 @@ + + diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java index 83727bcdc6..ca485f5da5 100644 --- a/Tethering/src/android/net/ip/IpServer.java +++ b/Tethering/src/android/net/ip/IpServer.java @@ -227,6 +227,7 @@ public class IpServer extends StateMachine { private final int mInterfaceType; private final LinkProperties mLinkProperties; private final boolean mUsingLegacyDhcp; + private final boolean mUsingBpfOffload; private final Dependencies mDeps; @@ -304,7 +305,8 @@ public class IpServer extends StateMachine { public IpServer( String ifaceName, Looper looper, int interfaceType, SharedLog log, - INetd netd, Callback callback, boolean usingLegacyDhcp, Dependencies deps) { + INetd netd, Callback callback, boolean usingLegacyDhcp, boolean usingBpfOffload, + Dependencies deps) { super(ifaceName, looper); mLog = log.forSubComponent(ifaceName); mNetd = netd; @@ -314,6 +316,7 @@ public class IpServer extends StateMachine { mInterfaceType = interfaceType; mLinkProperties = new LinkProperties(); mUsingLegacyDhcp = usingLegacyDhcp; + mUsingBpfOffload = usingBpfOffload; mDeps = deps; resetLinkProperties(); mLastError = TetheringManager.TETHER_ERROR_NO_ERROR; @@ -321,8 +324,15 @@ public class IpServer extends StateMachine { mIpNeighborMonitor = mDeps.getIpNeighborMonitor(getHandler(), mLog, new MyNeighborEventConsumer()); - if (!mIpNeighborMonitor.start()) { - mLog.e("Failed to create IpNeighborMonitor on " + mIfaceName); + + // IP neighbor monitor monitors the neighbor event for adding/removing offload + // forwarding rules per client. If BPF offload is not supported, don't start listening + // neighbor events. See updateIpv6ForwardingRules, addIpv6ForwardingRule, + // removeIpv6ForwardingRule. + if (mUsingBpfOffload) { + if (!mIpNeighborMonitor.start()) { + mLog.e("Failed to create IpNeighborMonitor on " + mIfaceName); + } } mInitialState = new InitialState(); @@ -715,12 +725,12 @@ public class IpServer extends StateMachine { final String upstreamIface = v6only.getInterfaceName(); params = new RaParams(); - // We advertise an mtu lower by 16, which is the closest multiple of 8 >= 14, - // the ethernet header size. This makes kernel ebpf tethering offload happy. - // This hack should be reverted once we have the kernel fixed up. + // When BPF offload is enabled, we advertise an mtu lower by 16, which is the closest + // multiple of 8 >= 14, the ethernet header size. This makes kernel ebpf tethering + // offload happy. This hack should be reverted once we have the kernel fixed up. // Note: this will automatically clamp to at least 1280 (ipv6 minimum mtu) // see RouterAdvertisementDaemon.java putMtu() - params.mtu = v6only.getMtu() - 16; + params.mtu = mUsingBpfOffload ? v6only.getMtu() - 16 : v6only.getMtu(); params.hasDefaultRoute = v6only.hasIpv6DefaultRoute(); if (params.hasDefaultRoute) params.hopLimit = getHopLimit(upstreamIface); @@ -844,6 +854,11 @@ public class IpServer extends StateMachine { } private void addIpv6ForwardingRule(Ipv6ForwardingRule rule) { + // Theoretically, we don't need this check because IP neighbor monitor doesn't start if BPF + // offload is disabled. Add this check just in case. + // TODO: Perhaps remove this protection check. + if (!mUsingBpfOffload) return; + try { mNetd.tetherOffloadRuleAdd(rule.toTetherOffloadRuleParcel()); mIpv6ForwardingRules.put(rule.address, rule); @@ -853,6 +868,11 @@ public class IpServer extends StateMachine { } private void removeIpv6ForwardingRule(Ipv6ForwardingRule rule, boolean removeFromMap) { + // Theoretically, we don't need this check because IP neighbor monitor doesn't start if BPF + // offload is disabled. Add this check just in case. + // TODO: Perhaps remove this protection check. + if (!mUsingBpfOffload) return; + try { mNetd.tetherOffloadRuleRemove(rule.toTetherOffloadRuleParcel()); if (removeFromMap) { diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java index ae6119f24f..2a5e6200e5 100644 --- a/Tethering/src/com/android/networkstack/tethering/Tethering.java +++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java @@ -2296,7 +2296,7 @@ public class Tethering { final TetherState tetherState = new TetherState( new IpServer(iface, mLooper, interfaceType, mLog, mNetd, makeControlCallback(), mConfig.enableLegacyDhcpServer, - mDeps.getIpServerDependencies())); + mConfig.enableBpfOffload, mDeps.getIpServerDependencies())); mTetherStates.put(iface, tetherState); tetherState.ipServer.start(); } diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java index 9d4e747327..91a6e29a05 100644 --- a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java @@ -72,6 +72,12 @@ public class TetheringConfiguration { private static final String[] DEFAULT_IPV4_DNS = {"8.8.4.4", "8.8.8.8"}; + /** + * Override enabling BPF offload configuration for tethering. + */ + public static final String OVERRIDE_TETHER_ENABLE_BPF_OFFLOAD = + "override_tether_enable_bpf_offload"; + /** * Use the old dnsmasq DHCP server for tethering instead of the framework implementation. */ @@ -95,6 +101,8 @@ public class TetheringConfiguration { public final String[] legacyDhcpRanges; public final String[] defaultIPv4DNS; public final boolean enableLegacyDhcpServer; + // TODO: Add to TetheringConfigurationParcel if required. + public final boolean enableBpfOffload; public final String[] provisioningApp; public final String provisioningAppNoUi; @@ -124,11 +132,12 @@ public class TetheringConfiguration { isDunRequired = checkDunRequired(ctx); chooseUpstreamAutomatically = getResourceBoolean( - res, R.bool.config_tether_upstream_automatic); + res, R.bool.config_tether_upstream_automatic, false /** default value */); preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(res, isDunRequired); legacyDhcpRanges = getLegacyDhcpRanges(res); defaultIPv4DNS = copy(DEFAULT_IPV4_DNS); + enableBpfOffload = getEnableBpfOffload(res); enableLegacyDhcpServer = getEnableLegacyDhcpServer(res); provisioningApp = getResourceStringArray(res, R.array.config_mobile_hotspot_provision_app); @@ -208,6 +217,9 @@ public class TetheringConfiguration { pw.print("provisioningAppNoUi: "); pw.println(provisioningAppNoUi); + pw.print("enableBpfOffload: "); + pw.println(enableBpfOffload); + pw.print("enableLegacyDhcpServer: "); pw.println(enableLegacyDhcpServer); } @@ -228,6 +240,7 @@ public class TetheringConfiguration { toIntArray(preferredUpstreamIfaceTypes))); sj.add(String.format("provisioningApp:%s", makeString(provisioningApp))); sj.add(String.format("provisioningAppNoUi:%s", provisioningAppNoUi)); + sj.add(String.format("enableBpfOffload:%s", enableBpfOffload)); sj.add(String.format("enableLegacyDhcpServer:%s", enableLegacyDhcpServer)); return String.format("TetheringConfiguration{%s}", sj.toString()); } @@ -332,11 +345,11 @@ public class TetheringConfiguration { } } - private static boolean getResourceBoolean(Resources res, int resId) { + private static boolean getResourceBoolean(Resources res, int resId, boolean defaultValue) { try { return res.getBoolean(resId); } catch (Resources.NotFoundException e404) { - return false; + return defaultValue; } } @@ -357,8 +370,29 @@ public class TetheringConfiguration { } } + private boolean getEnableBpfOffload(final Resources res) { + // Get BPF offload config + // Priority 1: Device config + // Priority 2: Resource config + // Priority 3: Default value + final boolean resourceValue = getResourceBoolean( + res, R.bool.config_tether_enable_bpf_offload, true /** default value */); + + // Due to the limitation of static mock for testing, using #getProperty directly instead + // of getDeviceConfigBoolean. getDeviceConfigBoolean is not invoked because it uses + // #getBoolean to get the boolean device config. The test can't know that the returned + // boolean value comes from device config or default value (because of null property + // string). Because the test would like to verify null property boolean string case, + // use DeviceConfig.getProperty here. See also the test case testBpfOffload{*} in + // TetheringConfigurationTest.java. + final String value = DeviceConfig.getProperty( + NAMESPACE_CONNECTIVITY, OVERRIDE_TETHER_ENABLE_BPF_OFFLOAD); + return (value != null) ? Boolean.parseBoolean(value) : resourceValue; + } + private boolean getEnableLegacyDhcpServer(final Resources res) { - return getResourceBoolean(res, R.bool.config_tether_enable_legacy_dhcp_server) + return getResourceBoolean( + res, R.bool.config_tether_enable_legacy_dhcp_server, false /** default value */) || getDeviceConfigBoolean(TETHER_ENABLE_LEGACY_DHCP_SERVER); } diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java index f9be7b9d36..b9622da9d2 100644 --- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java +++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java @@ -106,6 +106,7 @@ public class IpServerTest { private static final String BLUETOOTH_IFACE_ADDR = "192.168.42.1"; private static final int BLUETOOTH_DHCP_PREFIX_LENGTH = 24; private static final int DHCP_LEASE_TIME_SECS = 3600; + private static final boolean DEFAULT_USING_BPF_OFFLOAD = true; private static final InterfaceParams TEST_IFACE_PARAMS = new InterfaceParams( IFACE_NAME, 42 /* index */, MacAddress.ALL_ZEROS_ADDRESS, 1500 /* defaultMtu */); @@ -130,10 +131,11 @@ public class IpServerTest { private NeighborEventConsumer mNeighborEventConsumer; private void initStateMachine(int interfaceType) throws Exception { - initStateMachine(interfaceType, false /* usingLegacyDhcp */); + initStateMachine(interfaceType, false /* usingLegacyDhcp */, DEFAULT_USING_BPF_OFFLOAD); } - private void initStateMachine(int interfaceType, boolean usingLegacyDhcp) throws Exception { + private void initStateMachine(int interfaceType, boolean usingLegacyDhcp, + boolean usingBpfOffload) throws Exception { doAnswer(inv -> { final IDhcpServerCallbacks cb = inv.getArgument(2); new Thread(() -> { @@ -165,7 +167,7 @@ public class IpServerTest { mIpServer = new IpServer( IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd, - mCallback, usingLegacyDhcp, mDependencies); + mCallback, usingLegacyDhcp, usingBpfOffload, mDependencies); mIpServer.start(); mNeighborEventConsumer = neighborCaptor.getValue(); @@ -179,12 +181,13 @@ public class IpServerTest { private void initTetheredStateMachine(int interfaceType, String upstreamIface) throws Exception { - initTetheredStateMachine(interfaceType, upstreamIface, false); + initTetheredStateMachine(interfaceType, upstreamIface, false, + DEFAULT_USING_BPF_OFFLOAD); } private void initTetheredStateMachine(int interfaceType, String upstreamIface, - boolean usingLegacyDhcp) throws Exception { - initStateMachine(interfaceType, usingLegacyDhcp); + boolean usingLegacyDhcp, boolean usingBpfOffload) throws Exception { + initStateMachine(interfaceType, usingLegacyDhcp, usingBpfOffload); dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED); if (upstreamIface != null) { LinkProperties lp = new LinkProperties(); @@ -204,7 +207,8 @@ public class IpServerTest { when(mDependencies.getIpNeighborMonitor(any(), any(), any())) .thenReturn(mIpNeighborMonitor); mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), TETHERING_BLUETOOTH, mSharedLog, - mNetd, mCallback, false /* usingLegacyDhcp */, mDependencies); + mNetd, mCallback, false /* usingLegacyDhcp */, DEFAULT_USING_BPF_OFFLOAD, + mDependencies); mIpServer.start(); mLooper.dispatchAll(); verify(mCallback).updateInterfaceState( @@ -494,7 +498,8 @@ public class IpServerTest { @Test public void doesNotStartDhcpServerIfDisabled() throws Exception { - initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, true /* usingLegacyDhcp */); + initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, true /* usingLegacyDhcp */, + DEFAULT_USING_BPF_OFFLOAD); dispatchTetherConnectionChanged(UPSTREAM_IFACE); verify(mDependencies, never()).makeDhcpServer(any(), any(), any()); @@ -577,7 +582,8 @@ public class IpServerTest { @Test public void addRemoveipv6ForwardingRules() throws Exception { - initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */); + initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */, + DEFAULT_USING_BPF_OFFLOAD); final int myIfindex = TEST_IFACE_PARAMS.index; final int notMyIfindex = myIfindex - 1; @@ -678,6 +684,53 @@ public class IpServerTest { reset(mNetd); } + @Test + public void enableDisableUsingBpfOffload() throws Exception { + final int myIfindex = TEST_IFACE_PARAMS.index; + final InetAddress neigh = InetAddresses.parseNumericAddress("2001:db8::1"); + final MacAddress macA = MacAddress.fromString("00:00:00:00:00:0a"); + final MacAddress macNull = MacAddress.fromString("00:00:00:00:00:00"); + + reset(mNetd); + + // Expect that rules can be only added/removed when the BPF offload config is enabled. + // Note that the usingBpfOffload false case is not a realistic test case. Because IP + // neighbor monitor doesn't start if BPF offload is disabled, there should have no + // neighbor event listening. This is used for testing the protection check just in case. + // TODO: Perhaps remove this test once we don't need this check anymore. + for (boolean usingBpfOffload : new boolean[]{true, false}) { + initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */, + usingBpfOffload); + + // A neighbor is added. + recvNewNeigh(myIfindex, neigh, NUD_REACHABLE, macA); + if (usingBpfOffload) { + verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neigh, macA)); + } else { + verify(mNetd, never()).tetherOffloadRuleAdd(any()); + } + reset(mNetd); + + // A neighbor is deleted. + recvDelNeigh(myIfindex, neigh, NUD_STALE, macA); + if (usingBpfOffload) { + verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neigh, macNull)); + } else { + verify(mNetd, never()).tetherOffloadRuleRemove(any()); + } + reset(mNetd); + } + } + + @Test + public void doesNotStartIpNeighborMonitorIfBpfOffloadDisabled() throws Exception { + initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */, + false /* usingBpfOffload */); + + // IP neighbor monitor doesn't start if BPF offload is disabled. + verify(mIpNeighborMonitor, never()).start(); + } + private void assertDhcpStarted(IpPrefix expectedPrefix) throws Exception { verify(mDependencies, times(1)).makeDhcpServer(eq(IFACE_NAME), any(), any()); verify(mDhcpServer, timeout(MAKE_DHCPSERVER_TIMEOUT_MS).times(1)).startWithCallbacks( diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java index e8ba5b8168..fbfa871f76 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java @@ -127,6 +127,8 @@ public class TetheringConfigurationTest { .thenReturn(new String[0]); when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( false); + initializeBpfOffloadConfiguration(true, null /* unset */); + mHasTelephonyManager = true; mMockContext = new MockContext(mContext); mEnableLegacyDhcpServer = false; @@ -278,6 +280,50 @@ public class TetheringConfigurationTest { assertFalse(upstreamIterator.hasNext()); } + private void initializeBpfOffloadConfiguration( + final boolean fromRes, final String fromDevConfig) { + when(mResources.getBoolean(R.bool.config_tether_enable_bpf_offload)).thenReturn(fromRes); + doReturn(fromDevConfig).when( + () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY), + eq(TetheringConfiguration.OVERRIDE_TETHER_ENABLE_BPF_OFFLOAD))); + } + + @Test + public void testBpfOffloadEnabledByResource() { + initializeBpfOffloadConfiguration(true, null /* unset */); + final TetheringConfiguration enableByRes = + new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); + assertTrue(enableByRes.enableBpfOffload); + } + + @Test + public void testBpfOffloadEnabledByDeviceConfigOverride() { + for (boolean res : new boolean[]{true, false}) { + initializeBpfOffloadConfiguration(res, "true"); + final TetheringConfiguration enableByDevConOverride = + new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); + assertTrue(enableByDevConOverride.enableBpfOffload); + } + } + + @Test + public void testBpfOffloadDisabledByResource() { + initializeBpfOffloadConfiguration(false, null /* unset */); + final TetheringConfiguration disableByRes = + new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); + assertFalse(disableByRes.enableBpfOffload); + } + + @Test + public void testBpfOffloadDisabledByDeviceConfigOverride() { + for (boolean res : new boolean[]{true, false}) { + initializeBpfOffloadConfiguration(res, "false"); + final TetheringConfiguration disableByDevConOverride = + new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); + assertFalse(disableByDevConOverride.enableBpfOffload); + } + } + @Test public void testNewDhcpServerDisabled() { when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( From 7c23d31127edb75733b9da6fcd3fc48d89d1befd Mon Sep 17 00:00:00 2001 From: Mark Chien Date: Wed, 13 May 2020 09:13:59 +0000 Subject: [PATCH 136/188] Add test for OffloadHardwareInterface Bug: 145490751 Test: atest TetheringTests Merged-In: Ia402a6caaa0dfaa05d25a02101c515bbd884d33f Change-Id: Ia402a6caaa0dfaa05d25a02101c515bbd884d33f --- .../tethering/OffloadHardwareInterface.java | 124 +++++----- .../OffloadHardwareInterfaceTest.java | 215 ++++++++++++++++++ 2 files changed, 288 insertions(+), 51 deletions(-) create mode 100644 Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java diff --git a/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java b/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java index 293f8eae32..fe92204c25 100644 --- a/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java +++ b/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java @@ -66,6 +66,7 @@ public class OffloadHardwareInterface { private final Handler mHandler; private final SharedLog mLog; + private final Dependencies mDeps; private IOffloadControl mOffloadControl; private TetheringOffloadCallback mTetheringOffloadCallback; private ControlCallback mControlCallback; @@ -126,8 +127,76 @@ public class OffloadHardwareInterface { } public OffloadHardwareInterface(Handler h, SharedLog log) { + this(h, log, new Dependencies(log)); + } + + OffloadHardwareInterface(Handler h, SharedLog log, Dependencies deps) { mHandler = h; mLog = log.forSubComponent(TAG); + mDeps = deps; + } + + /** Capture OffloadHardwareInterface dependencies, for injection. */ + static class Dependencies { + private final SharedLog mLog; + + Dependencies(SharedLog log) { + mLog = log; + } + + public IOffloadConfig getOffloadConfig() { + try { + return IOffloadConfig.getService(true /*retry*/); + } catch (RemoteException | NoSuchElementException e) { + mLog.e("getIOffloadConfig error " + e); + return null; + } + } + + public IOffloadControl getOffloadControl() { + try { + return IOffloadControl.getService(true /*retry*/); + } catch (RemoteException | NoSuchElementException e) { + mLog.e("tethering offload control not supported: " + e); + return null; + } + } + + public NativeHandle createConntrackSocket(final int groups) { + final FileDescriptor fd; + try { + fd = NetlinkSocket.forProto(OsConstants.NETLINK_NETFILTER); + } catch (ErrnoException e) { + mLog.e("Unable to create conntrack socket " + e); + return null; + } + + final SocketAddress sockAddr = SocketUtils.makeNetlinkSocketAddress(0, groups); + try { + Os.bind(fd, sockAddr); + } catch (ErrnoException | SocketException e) { + mLog.e("Unable to bind conntrack socket for groups " + groups + " error: " + e); + try { + SocketUtils.closeSocket(fd); + } catch (IOException ie) { + // Nothing we can do here + } + return null; + } + try { + Os.connect(fd, sockAddr); + } catch (ErrnoException | SocketException e) { + mLog.e("connect to kernel fail for groups " + groups + " error: " + e); + try { + SocketUtils.closeSocket(fd); + } catch (IOException ie) { + // Nothing we can do here + } + return null; + } + + return new NativeHandle(fd, true); + } } /** Get default value indicating whether offload is supported. */ @@ -141,13 +210,7 @@ public class OffloadHardwareInterface { * share them with offload management process. */ public boolean initOffloadConfig() { - IOffloadConfig offloadConfig; - try { - offloadConfig = IOffloadConfig.getService(true /*retry*/); - } catch (RemoteException | NoSuchElementException e) { - mLog.e("getIOffloadConfig error " + e); - return false; - } + final IOffloadConfig offloadConfig = mDeps.getOffloadConfig(); if (offloadConfig == null) { mLog.e("Could not find IOffloadConfig service"); return false; @@ -159,11 +222,11 @@ public class OffloadHardwareInterface { // // h2 provides a file descriptor bound to the following netlink groups // (NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY). - final NativeHandle h1 = createConntrackSocket( + final NativeHandle h1 = mDeps.createConntrackSocket( NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY); if (h1 == null) return false; - final NativeHandle h2 = createConntrackSocket( + final NativeHandle h2 = mDeps.createConntrackSocket( NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY); if (h2 == null) { closeFdInNativeHandle(h1); @@ -198,53 +261,12 @@ public class OffloadHardwareInterface { } } - private NativeHandle createConntrackSocket(final int groups) { - FileDescriptor fd; - try { - fd = NetlinkSocket.forProto(OsConstants.NETLINK_NETFILTER); - } catch (ErrnoException e) { - mLog.e("Unable to create conntrack socket " + e); - return null; - } - - final SocketAddress sockAddr = SocketUtils.makeNetlinkSocketAddress(0, groups); - try { - Os.bind(fd, sockAddr); - } catch (ErrnoException | SocketException e) { - mLog.e("Unable to bind conntrack socket for groups " + groups + " error: " + e); - try { - SocketUtils.closeSocket(fd); - } catch (IOException ie) { - // Nothing we can do here - } - return null; - } - try { - Os.connect(fd, sockAddr); - } catch (ErrnoException | SocketException e) { - mLog.e("connect to kernel fail for groups " + groups + " error: " + e); - try { - SocketUtils.closeSocket(fd); - } catch (IOException ie) { - // Nothing we can do here - } - return null; - } - - return new NativeHandle(fd, true); - } - /** Initialize the tethering offload HAL. */ public boolean initOffloadControl(ControlCallback controlCb) { mControlCallback = controlCb; if (mOffloadControl == null) { - try { - mOffloadControl = IOffloadControl.getService(true /*retry*/); - } catch (RemoteException | NoSuchElementException e) { - mLog.e("tethering offload control not supported: " + e); - return false; - } + mOffloadControl = mDeps.getOffloadControl(); if (mOffloadControl == null) { mLog.e("tethering IOffloadControl.getService() returned null"); return false; diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java new file mode 100644 index 0000000000..f8ff1cb29c --- /dev/null +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.networkstack.tethering; + +import static android.net.util.TetheringUtils.uint16; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import android.hardware.tetheroffload.config.V1_0.IOffloadConfig; +import android.hardware.tetheroffload.control.V1_0.IOffloadControl; +import android.hardware.tetheroffload.control.V1_0.ITetheringOffloadCallback; +import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate; +import android.hardware.tetheroffload.control.V1_0.NetworkProtocol; +import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent; +import android.net.util.SharedLog; +import android.os.Handler; +import android.os.NativeHandle; +import android.os.test.TestLooper; +import android.system.OsConstants; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public final class OffloadHardwareInterfaceTest { + private static final String RMNET0 = "test_rmnet_data0"; + + private final TestLooper mTestLooper = new TestLooper(); + + private OffloadHardwareInterface mOffloadHw; + private ITetheringOffloadCallback mTetheringOffloadCallback; + private OffloadHardwareInterface.ControlCallback mControlCallback; + + @Mock private IOffloadConfig mIOffloadConfig; + @Mock private IOffloadControl mIOffloadControl; + @Mock private NativeHandle mNativeHandle; + + class MyDependencies extends OffloadHardwareInterface.Dependencies { + MyDependencies(SharedLog log) { + super(log); + } + + @Override + public IOffloadConfig getOffloadConfig() { + return mIOffloadConfig; + } + + @Override + public IOffloadControl getOffloadControl() { + return mIOffloadControl; + } + + @Override + public NativeHandle createConntrackSocket(final int groups) { + return mNativeHandle; + } + } + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + final SharedLog log = new SharedLog("test"); + mOffloadHw = new OffloadHardwareInterface(new Handler(mTestLooper.getLooper()), log, + new MyDependencies(log)); + mControlCallback = spy(new OffloadHardwareInterface.ControlCallback()); + } + + private void startOffloadHardwareInterface() throws Exception { + mOffloadHw.initOffloadConfig(); + mOffloadHw.initOffloadControl(mControlCallback); + final ArgumentCaptor mOffloadCallbackCaptor = + ArgumentCaptor.forClass(ITetheringOffloadCallback.class); + verify(mIOffloadControl).initOffload(mOffloadCallbackCaptor.capture(), any()); + mTetheringOffloadCallback = mOffloadCallbackCaptor.getValue(); + } + + @Test + public void testGetForwardedStats() throws Exception { + startOffloadHardwareInterface(); + final OffloadHardwareInterface.ForwardedStats stats = mOffloadHw.getForwardedStats(RMNET0); + verify(mIOffloadControl).getForwardedStats(eq(RMNET0), any()); + assertNotNull(stats); + } + + @Test + public void testSetLocalPrefixes() throws Exception { + startOffloadHardwareInterface(); + final ArrayList localPrefixes = new ArrayList<>(); + localPrefixes.add("127.0.0.0/8"); + localPrefixes.add("fe80::/64"); + mOffloadHw.setLocalPrefixes(localPrefixes); + verify(mIOffloadControl).setLocalPrefixes(eq(localPrefixes), any()); + } + + @Test + public void testSetDataLimit() throws Exception { + startOffloadHardwareInterface(); + final long limit = 12345; + mOffloadHw.setDataLimit(RMNET0, limit); + verify(mIOffloadControl).setDataLimit(eq(RMNET0), eq(limit), any()); + } + + @Test + public void testSetUpstreamParameters() throws Exception { + startOffloadHardwareInterface(); + final String v4addr = "192.168.10.1"; + final String v4gateway = "192.168.10.255"; + final ArrayList v6gws = new ArrayList<>(0); + v6gws.add("2001:db8::1"); + mOffloadHw.setUpstreamParameters(RMNET0, v4addr, v4gateway, v6gws); + verify(mIOffloadControl).setUpstreamParameters(eq(RMNET0), eq(v4addr), eq(v4gateway), + eq(v6gws), any()); + + final ArgumentCaptor> mArrayListCaptor = + ArgumentCaptor.forClass(ArrayList.class); + mOffloadHw.setUpstreamParameters(null, null, null, null); + verify(mIOffloadControl).setUpstreamParameters(eq(""), eq(""), eq(""), + mArrayListCaptor.capture(), any()); + assertEquals(mArrayListCaptor.getValue().size(), 0); + } + + @Test + public void testUpdateDownstreamPrefix() throws Exception { + startOffloadHardwareInterface(); + final String ifName = "wlan1"; + final String prefix = "192.168.43.0/24"; + mOffloadHw.addDownstreamPrefix(ifName, prefix); + verify(mIOffloadControl).addDownstream(eq(ifName), eq(prefix), any()); + + mOffloadHw.removeDownstreamPrefix(ifName, prefix); + verify(mIOffloadControl).removeDownstream(eq(ifName), eq(prefix), any()); + } + + @Test + public void testTetheringOffloadCallback() throws Exception { + startOffloadHardwareInterface(); + + mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STARTED); + mTestLooper.dispatchAll(); + verify(mControlCallback).onStarted(); + + mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_ERROR); + mTestLooper.dispatchAll(); + verify(mControlCallback).onStoppedError(); + + mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_UNSUPPORTED); + mTestLooper.dispatchAll(); + verify(mControlCallback).onStoppedUnsupported(); + + mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_SUPPORT_AVAILABLE); + mTestLooper.dispatchAll(); + verify(mControlCallback).onSupportAvailable(); + + mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_LIMIT_REACHED); + mTestLooper.dispatchAll(); + verify(mControlCallback).onStoppedLimitReached(); + + final NatTimeoutUpdate tcpParams = buildNatTimeoutUpdate(NetworkProtocol.TCP); + mTetheringOffloadCallback.updateTimeout(tcpParams); + mTestLooper.dispatchAll(); + verify(mControlCallback).onNatTimeoutUpdate(eq(OsConstants.IPPROTO_TCP), + eq(tcpParams.src.addr), + eq(uint16(tcpParams.src.port)), + eq(tcpParams.dst.addr), + eq(uint16(tcpParams.dst.port))); + + final NatTimeoutUpdate udpParams = buildNatTimeoutUpdate(NetworkProtocol.UDP); + mTetheringOffloadCallback.updateTimeout(udpParams); + mTestLooper.dispatchAll(); + verify(mControlCallback).onNatTimeoutUpdate(eq(OsConstants.IPPROTO_UDP), + eq(udpParams.src.addr), + eq(uint16(udpParams.src.port)), + eq(udpParams.dst.addr), + eq(uint16(udpParams.dst.port))); + } + + private NatTimeoutUpdate buildNatTimeoutUpdate(final int proto) { + final NatTimeoutUpdate params = new NatTimeoutUpdate(); + params.proto = proto; + params.src.addr = "192.168.43.200"; + params.src.port = 100; + params.dst.addr = "172.50.46.169"; + params.dst.port = 150; + return params; + } +} From fd8b4bbe35b24b2b89cf154cfc2f137754212b35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Tue, 12 May 2020 19:04:39 +0000 Subject: [PATCH 137/188] A minor followup change for BPF offload device option stuff - Correct description and spelling in the code and xml files. - Add a TODO for refactoring the IpServer constructor. - Refine the if-statement for starting IP neighbor monitor. Bug: 149997301 Test: atest IpServerTest Original-Change: https://android-review.googlesource.com/1309273 Merged-In: If9c8bc6f785fa80575db56de4e223292e9807ace Change-Id: If9c8bc6f785fa80575db56de4e223292e9807ace --- Tethering/res/values/config.xml | 2 +- Tethering/res/values/overlayable.xml | 2 +- Tethering/src/android/net/ip/IpServer.java | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Tethering/res/values/config.xml b/Tethering/res/values/config.xml index 6604db641c..9269c6f0fd 100644 --- a/Tethering/res/values/config.xml +++ b/Tethering/res/values/config.xml @@ -59,7 +59,7 @@ true diff --git a/Tethering/res/values/overlayable.xml b/Tethering/res/values/overlayable.xml index 288dd5ddf3..4e2bb1e31b 100644 --- a/Tethering/res/values/overlayable.xml +++ b/Tethering/res/values/overlayable.xml @@ -25,7 +25,7 @@ diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java index ca485f5da5..d993306b17 100644 --- a/Tethering/src/android/net/ip/IpServer.java +++ b/Tethering/src/android/net/ip/IpServer.java @@ -303,6 +303,8 @@ public class IpServer extends StateMachine { private final IpNeighborMonitor mIpNeighborMonitor; + // TODO: Add a dependency object to pass the data members or variables from the tethering + // object. It helps to reduce the arguments of the constructor. public IpServer( String ifaceName, Looper looper, int interfaceType, SharedLog log, INetd netd, Callback callback, boolean usingLegacyDhcp, boolean usingBpfOffload, @@ -325,14 +327,12 @@ public class IpServer extends StateMachine { mIpNeighborMonitor = mDeps.getIpNeighborMonitor(getHandler(), mLog, new MyNeighborEventConsumer()); - // IP neighbor monitor monitors the neighbor event for adding/removing offload + // IP neighbor monitor monitors the neighbor events for adding/removing offload // forwarding rules per client. If BPF offload is not supported, don't start listening - // neighbor events. See updateIpv6ForwardingRules, addIpv6ForwardingRule, + // for neighbor events. See updateIpv6ForwardingRules, addIpv6ForwardingRule, // removeIpv6ForwardingRule. - if (mUsingBpfOffload) { - if (!mIpNeighborMonitor.start()) { - mLog.e("Failed to create IpNeighborMonitor on " + mIfaceName); - } + if (mUsingBpfOffload && !mIpNeighborMonitor.start()) { + mLog.e("Failed to create IpNeighborMonitor on " + mIfaceName); } mInitialState = new InitialState(); From b016744a1a7ff8538756677fac4c52832da21c0c Mon Sep 17 00:00:00 2001 From: Paul Duffin Date: Wed, 13 May 2020 09:56:18 +0100 Subject: [PATCH 138/188] Cleanup packages/Tethering/common/TetheringLib Merge the otherwise unused tethering-aidl-interfaces into framework-tethering. This is in preparation for converting to use java_sdk_library. Bug: 155164730 Test: m droid Merged-In: I4583539d11ba69320aa5a0dfcfee072c81affac2 Change-Id: I4583539d11ba69320aa5a0dfcfee072c81affac2 (cherry picked from commit 267dd95c3e93f75c42c3f4e5cf576829b528f6c2) --- Tethering/common/TetheringLib/Android.bp | 40 +----------------------- 1 file changed, 1 insertion(+), 39 deletions(-) diff --git a/Tethering/common/TetheringLib/Android.bp b/Tethering/common/TetheringLib/Android.bp index d03115a158..ae4bb3e5e2 100644 --- a/Tethering/common/TetheringLib/Android.bp +++ b/Tethering/common/TetheringLib/Android.bp @@ -13,49 +13,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -// AIDL interfaces between the core system and the tethering mainline module. -aidl_interface { - name: "tethering-aidl-interfaces", - unstable: true, - local_include_dir: "src", - include_dirs: ["frameworks/base/core/java"], // For framework parcelables. - srcs: [ - // @JavaOnlyStableParcelable aidl declarations must not be listed here, as this would cause - // compilation to fail (b/148001843). - "src/android/net/IIntResultListener.aidl", - "src/android/net/ITetheringConnector.aidl", - "src/android/net/ITetheringEventCallback.aidl", - "src/android/net/TetheringCallbackStartedParcel.aidl", - "src/android/net/TetheringConfigurationParcel.aidl", - "src/android/net/TetheringRequestParcel.aidl", - "src/android/net/TetherStatesParcel.aidl", - ], - backend: { - ndk: { - enabled: false, - }, - cpp: { - enabled: false, - }, - java: { - apex_available: [ - "//apex_available:platform", - "com.android.tethering", - ], - }, - }, -} - java_library { name: "framework-tethering", sdk_version: "module_current", srcs: [ - "src/android/net/TetheredClient.java", - "src/android/net/TetheringManager.java", - "src/android/net/TetheringConstants.java", - ], - static_libs: [ - "tethering-aidl-interfaces-java", + ":framework-tethering-srcs", ], jarjar_rules: "jarjar-rules.txt", installable: true, From c96e106d6834390b6b5cf1969554b9c3c6f83703 Mon Sep 17 00:00:00 2001 From: markchien Date: Tue, 12 May 2020 00:08:27 +0800 Subject: [PATCH 139/188] Make members final in TetheringService 1. Move isTetheringSupport logic from TetheringService to Tethering. 2. Small readability improvement in TetheringTest. Also change config_tether_upstream_automatic from false to true in TetheringTest. So TetheringTests would default run automatic select upstream flow instead of selecting by legacy perferred network type list. Bug: 153609486 Test: atest TetheringTest Change-Id: I5a82a6347f62d3a7031db5c56e8e0c8530dafd8f Merged-In: I5a82a6347f62d3a7031db5c56e8e0c8530dafd8f --- .../networkstack/tethering/Tethering.java | 36 +++- .../tethering/TetheringService.java | 187 +++++++----------- .../tethering/TetheringServiceTest.java | 19 +- .../networkstack/tethering/TetheringTest.java | 56 +++--- 4 files changed, 140 insertions(+), 158 deletions(-) diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java index ae6119f24f..331748d8c6 100644 --- a/Tethering/src/com/android/networkstack/tethering/Tethering.java +++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java @@ -109,8 +109,10 @@ import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceSpecificException; +import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; +import android.provider.Settings; import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; import android.text.TextUtils; @@ -228,6 +230,7 @@ public class Tethering { private final ConnectedClientsTracker mConnectedClientsTracker; private final TetheringThreadExecutor mExecutor; private final TetheringNotificationUpdater mNotificationUpdater; + private final UserManager mUserManager; private int mActiveDataSubId = INVALID_SUBSCRIPTION_ID; // All the usage of mTetheringEventCallback should run in the same thread. private ITetheringEventCallback mTetheringEventCallback = null; @@ -305,23 +308,24 @@ public class Tethering { mStateReceiver = new StateReceiver(); - final UserManager userManager = (UserManager) mContext.getSystemService( - Context.USER_SERVICE); + mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); mTetheringRestriction = new UserRestrictionActionListener( - userManager, this, mNotificationUpdater); + mUserManager, this, mNotificationUpdater); mExecutor = new TetheringThreadExecutor(mHandler); mActiveDataSubIdListener = new ActiveDataSubIdListener(mExecutor); mNetdCallback = new NetdCallback(); // Load tethering configuration. updateConfiguration(); + + startStateMachineUpdaters(); } /** * Start to register callbacks. * Call this function when tethering is ready to handle callback events. */ - public void startStateMachineUpdaters() { + private void startStateMachineUpdaters() { try { mNetd.registerUnsolicitedEventListener(mNetdCallback); } catch (RemoteException e) { @@ -779,7 +783,7 @@ public class Tethering { // TODO: Figure out how to update for local hotspot mode interfaces. private void sendTetherStateChangedBroadcast() { - if (!mDeps.isTetheringSupported()) return; + if (!isTetheringSupported()) return; final ArrayList availableList = new ArrayList<>(); final ArrayList tetherList = new ArrayList<>(); @@ -1020,14 +1024,14 @@ public class Tethering { @VisibleForTesting protected static class UserRestrictionActionListener { - private final UserManager mUserManager; + private final UserManager mUserMgr; private final Tethering mWrapper; private final TetheringNotificationUpdater mNotificationUpdater; public boolean mDisallowTethering; public UserRestrictionActionListener(@NonNull UserManager um, @NonNull Tethering wrapper, @NonNull TetheringNotificationUpdater updater) { - mUserManager = um; + mUserMgr = um; mWrapper = wrapper; mNotificationUpdater = updater; mDisallowTethering = false; @@ -1037,7 +1041,7 @@ public class Tethering { // getUserRestrictions gets restriction for this process' user, which is the primary // user. This is fine because DISALLOW_CONFIG_TETHERING can only be set on the primary // user. See UserManager.DISALLOW_CONFIG_TETHERING. - final Bundle restrictions = mUserManager.getUserRestrictions(); + final Bundle restrictions = mUserMgr.getUserRestrictions(); final boolean newlyDisallowed = restrictions.getBoolean(UserManager.DISALLOW_CONFIG_TETHERING); final boolean prevDisallowed = mDisallowTethering; @@ -1988,7 +1992,7 @@ public class Tethering { mHandler.post(() -> { mTetheringEventCallbacks.register(callback, new CallbackCookie(hasListPermission)); final TetheringCallbackStartedParcel parcel = new TetheringCallbackStartedParcel(); - parcel.tetheringSupported = mDeps.isTetheringSupported(); + parcel.tetheringSupported = isTetheringSupported(); parcel.upstreamNetwork = mTetherUpstream; parcel.config = mConfig.toStableParcelable(); parcel.states = @@ -2111,6 +2115,20 @@ public class Tethering { } } + // if ro.tether.denied = true we default to no tethering + // gservices could set the secure setting to 1 though to enable it on a build where it + // had previously been turned off. + boolean isTetheringSupported() { + final int defaultVal = + SystemProperties.get("ro.tether.denied").equals("true") ? 0 : 1; + final boolean tetherSupported = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.TETHER_SUPPORTED, defaultVal) != 0; + final boolean tetherEnabledInSettings = tetherSupported + && !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING); + + return tetherEnabledInSettings && hasTetherableConfiguration(); + } + void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer, @Nullable String[] args) { // Binder.java closes the resource for us. @SuppressWarnings("resource") diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringService.java b/Tethering/src/com/android/networkstack/tethering/TetheringService.java index d07c555f66..bf7fb042ed 100644 --- a/Tethering/src/com/android/networkstack/tethering/TetheringService.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringService.java @@ -40,15 +40,12 @@ import android.net.TetheringRequestParcel; import android.net.dhcp.DhcpServerCallbacks; import android.net.dhcp.DhcpServingParamsParcel; import android.net.ip.IpServer; -import android.net.util.SharedLog; import android.os.Binder; import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.os.ResultReceiver; -import android.os.SystemProperties; -import android.os.UserManager; import android.provider.Settings; import android.util.Log; @@ -68,21 +65,14 @@ import java.io.PrintWriter; public class TetheringService extends Service { private static final String TAG = TetheringService.class.getSimpleName(); - private final SharedLog mLog = new SharedLog(TAG); private TetheringConnector mConnector; - private Context mContext; - private TetheringDependencies mDeps; - private Tethering mTethering; - private UserManager mUserManager; @Override public void onCreate() { - mLog.mark("onCreate"); - mDeps = getTetheringDependencies(); - mContext = mDeps.getContext(); - mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - mTethering = makeTethering(mDeps); - mTethering.startStateMachineUpdaters(); + final TetheringDependencies deps = makeTetheringDependencies(); + // The Tethering object needs a fully functional context to start, so this can't be done + // in the constructor. + mConnector = new TetheringConnector(makeTethering(deps), TetheringService.this); } /** @@ -94,21 +84,10 @@ public class TetheringService extends Service { return new Tethering(deps); } - /** - * Create a binder connector for the system server to communicate with the tethering. - */ - private synchronized IBinder makeConnector() { - if (mConnector == null) { - mConnector = new TetheringConnector(mTethering, TetheringService.this); - } - return mConnector; - } - @NonNull @Override public IBinder onBind(Intent intent) { - mLog.mark("onBind"); - return makeConnector(); + return mConnector; } private static class TetheringConnector extends ITetheringConnector.Stub { @@ -237,7 +216,7 @@ public class TetheringService extends Service { listener.onResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); return true; } - if (!mService.isTetheringSupported()) { + if (!mTethering.isTetheringSupported()) { listener.onResult(TETHER_ERROR_UNSUPPORTED); return true; } @@ -253,7 +232,7 @@ public class TetheringService extends Service { receiver.send(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION, null); return true; } - if (!mService.isTetheringSupported()) { + if (!mTethering.isTetheringSupported()) { receiver.send(TETHER_ERROR_UNSUPPORTED, null); return true; } @@ -287,105 +266,83 @@ public class TetheringService extends Service { } } - // if ro.tether.denied = true we default to no tethering - // gservices could set the secure setting to 1 though to enable it on a build where it - // had previously been turned off. - private boolean isTetheringSupported() { - final int defaultVal = - SystemProperties.get("ro.tether.denied").equals("true") ? 0 : 1; - final boolean tetherSupported = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.TETHER_SUPPORTED, defaultVal) != 0; - final boolean tetherEnabledInSettings = tetherSupported - && !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING); - - return tetherEnabledInSettings && mTethering.hasTetherableConfiguration(); - } - /** * An injection method for testing. */ @VisibleForTesting - public TetheringDependencies getTetheringDependencies() { - if (mDeps == null) { - mDeps = new TetheringDependencies() { - @Override - public NetworkRequest getDefaultNetworkRequest() { - // TODO: b/147280869, add a proper system API to replace this. - final NetworkRequest trackDefaultRequest = new NetworkRequest.Builder() - .clearCapabilities() - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) - .addCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED) - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) - .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) - .build(); - return trackDefaultRequest; - } + public TetheringDependencies makeTetheringDependencies() { + return new TetheringDependencies() { + @Override + public NetworkRequest getDefaultNetworkRequest() { + // TODO: b/147280869, add a proper system API to replace this. + final NetworkRequest trackDefaultRequest = new NetworkRequest.Builder() + .clearCapabilities() + .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) + .addCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED) + .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) + .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + .build(); + return trackDefaultRequest; + } - @Override - public Looper getTetheringLooper() { - final HandlerThread tetherThread = new HandlerThread("android.tethering"); - tetherThread.start(); - return tetherThread.getLooper(); - } + @Override + public Looper getTetheringLooper() { + final HandlerThread tetherThread = new HandlerThread("android.tethering"); + tetherThread.start(); + return tetherThread.getLooper(); + } - @Override - public boolean isTetheringSupported() { - return TetheringService.this.isTetheringSupported(); - } + @Override + public Context getContext() { + return TetheringService.this; + } - @Override - public Context getContext() { - return TetheringService.this; - } + @Override + public IpServer.Dependencies getIpServerDependencies() { + return new IpServer.Dependencies() { + @Override + public void makeDhcpServer(String ifName, DhcpServingParamsParcel params, + DhcpServerCallbacks cb) { + try { + final INetworkStackConnector service = getNetworkStackConnector(); + if (service == null) return; - @Override - public IpServer.Dependencies getIpServerDependencies() { - return new IpServer.Dependencies() { - @Override - public void makeDhcpServer(String ifName, DhcpServingParamsParcel params, - DhcpServerCallbacks cb) { + service.makeDhcpServer(ifName, params, cb); + } catch (RemoteException e) { + Log.e(TAG, "Fail to make dhcp server"); try { - final INetworkStackConnector service = getNetworkStackConnector(); - if (service == null) return; - - service.makeDhcpServer(ifName, params, cb); - } catch (RemoteException e) { - Log.e(TAG, "Fail to make dhcp server"); - try { - cb.onDhcpServerCreated(STATUS_UNKNOWN_ERROR, null); - } catch (RemoteException re) { } - } + cb.onDhcpServerCreated(STATUS_UNKNOWN_ERROR, null); + } catch (RemoteException re) { } } - }; - } - - // TODO: replace this by NetworkStackClient#getRemoteConnector after refactoring - // networkStackClient. - static final int NETWORKSTACK_TIMEOUT_MS = 60_000; - private INetworkStackConnector getNetworkStackConnector() { - IBinder connector; - try { - final long before = System.currentTimeMillis(); - while ((connector = NetworkStack.getService()) == null) { - if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) { - Log.wtf(TAG, "Timeout, fail to get INetworkStackConnector"); - return null; - } - Thread.sleep(200); - } - } catch (InterruptedException e) { - Log.wtf(TAG, "Interrupted, fail to get INetworkStackConnector"); - return null; } - return INetworkStackConnector.Stub.asInterface(connector); - } + }; + } - @Override - public BluetoothAdapter getBluetoothAdapter() { - return BluetoothAdapter.getDefaultAdapter(); + // TODO: replace this by NetworkStackClient#getRemoteConnector after refactoring + // networkStackClient. + static final int NETWORKSTACK_TIMEOUT_MS = 60_000; + private INetworkStackConnector getNetworkStackConnector() { + IBinder connector; + try { + final long before = System.currentTimeMillis(); + while ((connector = NetworkStack.getService()) == null) { + if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) { + Log.wtf(TAG, "Timeout, fail to get INetworkStackConnector"); + return null; + } + Thread.sleep(200); + } + } catch (InterruptedException e) { + Log.wtf(TAG, "Interrupted, fail to get INetworkStackConnector"); + return null; } - }; - } - return mDeps; + return INetworkStackConnector.Stub.asInterface(connector); + } + + @Override + public BluetoothAdapter getBluetoothAdapter() { + return BluetoothAdapter.getDefaultAdapter(); + } + }; } } diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java index 51bad9af23..4a667b1bdc 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java @@ -82,8 +82,7 @@ public final class TetheringServiceTest { mTetheringConnector = mockConnector.getTetheringConnector(); final MockTetheringService service = mockConnector.getService(); mTethering = service.getTethering(); - verify(mTethering).startStateMachineUpdaters(); - when(mTethering.hasTetherableConfiguration()).thenReturn(true); + when(mTethering.isTetheringSupported()).thenReturn(true); } @After @@ -96,7 +95,7 @@ public final class TetheringServiceTest { when(mTethering.tether(TEST_IFACE_NAME)).thenReturn(TETHER_ERROR_NO_ERROR); final TestTetheringResult result = new TestTetheringResult(); mTetheringConnector.tether(TEST_IFACE_NAME, TEST_CALLER_PKG, result); - verify(mTethering).hasTetherableConfiguration(); + verify(mTethering).isTetheringSupported(); verify(mTethering).tether(TEST_IFACE_NAME); verifyNoMoreInteractions(mTethering); result.assertResult(TETHER_ERROR_NO_ERROR); @@ -107,7 +106,7 @@ public final class TetheringServiceTest { when(mTethering.untether(TEST_IFACE_NAME)).thenReturn(TETHER_ERROR_NO_ERROR); final TestTetheringResult result = new TestTetheringResult(); mTetheringConnector.untether(TEST_IFACE_NAME, TEST_CALLER_PKG, result); - verify(mTethering).hasTetherableConfiguration(); + verify(mTethering).isTetheringSupported(); verify(mTethering).untether(TEST_IFACE_NAME); verifyNoMoreInteractions(mTethering); result.assertResult(TETHER_ERROR_NO_ERROR); @@ -118,7 +117,7 @@ public final class TetheringServiceTest { when(mTethering.setUsbTethering(true /* enable */)).thenReturn(TETHER_ERROR_NO_ERROR); final TestTetheringResult result = new TestTetheringResult(); mTetheringConnector.setUsbTethering(true /* enable */, TEST_CALLER_PKG, result); - verify(mTethering).hasTetherableConfiguration(); + verify(mTethering).isTetheringSupported(); verify(mTethering).setUsbTethering(true /* enable */); verifyNoMoreInteractions(mTethering); result.assertResult(TETHER_ERROR_NO_ERROR); @@ -130,7 +129,7 @@ public final class TetheringServiceTest { final TetheringRequestParcel request = new TetheringRequestParcel(); request.tetheringType = TETHERING_WIFI; mTetheringConnector.startTethering(request, TEST_CALLER_PKG, result); - verify(mTethering).hasTetherableConfiguration(); + verify(mTethering).isTetheringSupported(); verify(mTethering).startTethering(eq(request), eq(result)); verifyNoMoreInteractions(mTethering); } @@ -139,7 +138,7 @@ public final class TetheringServiceTest { public void testStopTethering() throws Exception { final TestTetheringResult result = new TestTetheringResult(); mTetheringConnector.stopTethering(TETHERING_WIFI, TEST_CALLER_PKG, result); - verify(mTethering).hasTetherableConfiguration(); + verify(mTethering).isTetheringSupported(); verify(mTethering).stopTethering(TETHERING_WIFI); verifyNoMoreInteractions(mTethering); result.assertResult(TETHER_ERROR_NO_ERROR); @@ -150,7 +149,7 @@ public final class TetheringServiceTest { final ResultReceiver result = new ResultReceiver(null); mTetheringConnector.requestLatestTetheringEntitlementResult(TETHERING_WIFI, result, true /* showEntitlementUi */, TEST_CALLER_PKG); - verify(mTethering).hasTetherableConfiguration(); + verify(mTethering).isTetheringSupported(); verify(mTethering).requestLatestTetheringEntitlementResult(eq(TETHERING_WIFI), eq(result), eq(true) /* showEntitlementUi */); verifyNoMoreInteractions(mTethering); @@ -177,7 +176,7 @@ public final class TetheringServiceTest { public void testStopAllTethering() throws Exception { final TestTetheringResult result = new TestTetheringResult(); mTetheringConnector.stopAllTethering(TEST_CALLER_PKG, result); - verify(mTethering).hasTetherableConfiguration(); + verify(mTethering).isTetheringSupported(); verify(mTethering).untetherAll(); verifyNoMoreInteractions(mTethering); result.assertResult(TETHER_ERROR_NO_ERROR); @@ -187,7 +186,7 @@ public final class TetheringServiceTest { public void testIsTetheringSupported() throws Exception { final TestTetheringResult result = new TestTetheringResult(); mTetheringConnector.isTetheringSupported(TEST_CALLER_PKG, result); - verify(mTethering).hasTetherableConfiguration(); + verify(mTethering).isTetheringSupported(); verifyNoMoreInteractions(mTethering); result.assertResult(TETHER_ERROR_NO_ERROR); } 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 2bd8ae0288..00174e6d8b 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java @@ -485,18 +485,6 @@ public class TetheringTest { MockitoAnnotations.initMocks(this); when(mResources.getStringArray(R.array.config_tether_dhcp_range)) .thenReturn(new String[0]); - when(mResources.getStringArray(R.array.config_tether_usb_regexs)) - .thenReturn(new String[] { "test_rndis\\d" }); - when(mResources.getStringArray(R.array.config_tether_wifi_regexs)) - .thenReturn(new String[]{ "test_wlan\\d" }); - when(mResources.getStringArray(R.array.config_tether_wifi_p2p_regexs)) - .thenReturn(new String[]{ "test_p2p-p2p\\d-.*" }); - when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs)) - .thenReturn(new String[0]); - when(mResources.getStringArray(R.array.config_tether_ncm_regexs)) - .thenReturn(new String[] { "test_ncm\\d" }); - when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[0]); - when(mResources.getBoolean(R.bool.config_tether_upstream_automatic)).thenReturn(false); when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( false); when(mNetd.interfaceGetList()) @@ -515,6 +503,7 @@ public class TetheringTest { mServiceContext = new TestContext(mContext); mContentResolver = new MockContentResolver(mServiceContext); mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); + setTetheringSupported(true /* supported */); mIntents = new Vector<>(); mBroadcastReceiver = new BroadcastReceiver() { @Override @@ -525,7 +514,6 @@ public class TetheringTest { mServiceContext.registerReceiver(mBroadcastReceiver, new IntentFilter(ACTION_TETHER_STATE_CHANGED)); mTethering = makeTethering(); - mTethering.startStateMachineUpdaters(); verify(mStatsManager, times(1)).registerNetworkStatsProvider(anyString(), any()); verify(mNetd).registerUnsolicitedEventListener(any()); final ArgumentCaptor phoneListenerCaptor = @@ -536,6 +524,31 @@ public class TetheringTest { mPhoneStateListener = phoneListenerCaptor.getValue(); } + private void setTetheringSupported(final boolean supported) { + Settings.Global.putInt(mContentResolver, Settings.Global.TETHER_SUPPORTED, + supported ? 1 : 0); + when(mUserManager.hasUserRestriction( + UserManager.DISALLOW_CONFIG_TETHERING)).thenReturn(!supported); + // Setup tetherable configuration. + when(mResources.getStringArray(R.array.config_tether_usb_regexs)) + .thenReturn(new String[] { "test_rndis\\d" }); + when(mResources.getStringArray(R.array.config_tether_wifi_regexs)) + .thenReturn(new String[]{ "test_wlan\\d" }); + when(mResources.getStringArray(R.array.config_tether_wifi_p2p_regexs)) + .thenReturn(new String[]{ "test_p2p-p2p\\d-.*" }); + when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs)) + .thenReturn(new String[0]); + when(mResources.getStringArray(R.array.config_tether_ncm_regexs)) + .thenReturn(new String[] { "test_ncm\\d" }); + when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[0]); + when(mResources.getBoolean(R.bool.config_tether_upstream_automatic)).thenReturn(true); + } + + private void initTetheringUpstream(UpstreamNetworkState upstreamState) { + when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState); + when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())).thenReturn(upstreamState); + } + private Tethering makeTethering() { mTetheringDependencies.reset(); return new Tethering(mTetheringDependencies); @@ -672,9 +685,7 @@ public class TetheringTest { } private void prepareUsbTethering(UpstreamNetworkState upstreamState) { - when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState); - when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())) - .thenReturn(upstreamState); + initTetheringUpstream(upstreamState); // Emulate pressing the USB tethering button in Settings UI. mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB), null); @@ -700,7 +711,7 @@ public class TetheringTest { verify(mNetd, times(1)).interfaceGetList(); // UpstreamNetworkMonitor should receive selected upstream - verify(mUpstreamNetworkMonitor, times(1)).selectPreferredUpstreamType(any()); + verify(mUpstreamNetworkMonitor, times(1)).getCurrentPreferredUpstream(); verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(upstreamState.network); } @@ -872,8 +883,7 @@ public class TetheringTest { // Then 464xlat comes up upstreamState = buildMobile464xlatUpstreamState(); - when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())) - .thenReturn(upstreamState); + initTetheringUpstream(upstreamState); // Upstream LinkProperties changed: UpstreamNetworkMonitor sends EVENT_ON_LINKPROPERTIES. mTetheringDependencies.mUpstreamNetworkMonitorMasterSM.sendMessage( @@ -1344,9 +1354,7 @@ public class TetheringTest { callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED); // 2. Enable wifi tethering. UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState(); - when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState); - when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())) - .thenReturn(upstreamState); + initTetheringUpstream(upstreamState); when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true); mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true); mLooper.dispatchAll(); @@ -1723,7 +1731,7 @@ public class TetheringTest { final Tethering.TetherMasterSM stateMachine = (Tethering.TetherMasterSM) mTetheringDependencies.mUpstreamNetworkMonitorMasterSM; final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState(); - when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())).thenReturn(upstreamState); + initTetheringUpstream(upstreamState); stateMachine.chooseUpstreamType(true); verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(eq(upstreamState.network)); @@ -1735,7 +1743,7 @@ public class TetheringTest { final Tethering.TetherMasterSM stateMachine = (Tethering.TetherMasterSM) mTetheringDependencies.mUpstreamNetworkMonitorMasterSM; final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState(); - when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())).thenReturn(upstreamState); + initTetheringUpstream(upstreamState); stateMachine.chooseUpstreamType(true); stateMachine.handleUpstreamNetworkMonitorCallback(EVENT_ON_CAPABILITIES, upstreamState); From bd2cbe6c736237926b7bb69926a176ccc9738351 Mon Sep 17 00:00:00 2001 From: markchien Date: Tue, 12 May 2020 00:08:27 +0800 Subject: [PATCH 140/188] Make members final in TetheringService 1. Move isTetheringSupport logic from TetheringService to Tethering. 2. Small readability improvement in TetheringTest. Also change config_tether_upstream_automatic from false to true in TetheringTest. So TetheringTests would default run automatic select upstream flow instead of selecting by legacy perferred network type list. Bug: 153609486 Test: atest TetheringTest Change-Id: I5a82a6347f62d3a7031db5c56e8e0c8530dafd8f Merged-In: I5a82a6347f62d3a7031db5c56e8e0c8530dafd8f (cherry picked from commit 569870320a0c468609ddd7d96dae1b5845b99205) --- .../networkstack/tethering/Tethering.java | 36 +++- .../tethering/TetheringService.java | 187 +++++++----------- .../tethering/TetheringServiceTest.java | 19 +- .../networkstack/tethering/TetheringTest.java | 56 +++--- 4 files changed, 140 insertions(+), 158 deletions(-) diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java index 2a5e6200e5..04ad43f6e2 100644 --- a/Tethering/src/com/android/networkstack/tethering/Tethering.java +++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java @@ -109,8 +109,10 @@ import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceSpecificException; +import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; +import android.provider.Settings; import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; import android.text.TextUtils; @@ -228,6 +230,7 @@ public class Tethering { private final ConnectedClientsTracker mConnectedClientsTracker; private final TetheringThreadExecutor mExecutor; private final TetheringNotificationUpdater mNotificationUpdater; + private final UserManager mUserManager; private int mActiveDataSubId = INVALID_SUBSCRIPTION_ID; // All the usage of mTetheringEventCallback should run in the same thread. private ITetheringEventCallback mTetheringEventCallback = null; @@ -305,23 +308,24 @@ public class Tethering { mStateReceiver = new StateReceiver(); - final UserManager userManager = (UserManager) mContext.getSystemService( - Context.USER_SERVICE); + mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); mTetheringRestriction = new UserRestrictionActionListener( - userManager, this, mNotificationUpdater); + mUserManager, this, mNotificationUpdater); mExecutor = new TetheringThreadExecutor(mHandler); mActiveDataSubIdListener = new ActiveDataSubIdListener(mExecutor); mNetdCallback = new NetdCallback(); // Load tethering configuration. updateConfiguration(); + + startStateMachineUpdaters(); } /** * Start to register callbacks. * Call this function when tethering is ready to handle callback events. */ - public void startStateMachineUpdaters() { + private void startStateMachineUpdaters() { try { mNetd.registerUnsolicitedEventListener(mNetdCallback); } catch (RemoteException e) { @@ -779,7 +783,7 @@ public class Tethering { // TODO: Figure out how to update for local hotspot mode interfaces. private void sendTetherStateChangedBroadcast() { - if (!mDeps.isTetheringSupported()) return; + if (!isTetheringSupported()) return; final ArrayList availableList = new ArrayList<>(); final ArrayList tetherList = new ArrayList<>(); @@ -1020,14 +1024,14 @@ public class Tethering { @VisibleForTesting protected static class UserRestrictionActionListener { - private final UserManager mUserManager; + private final UserManager mUserMgr; private final Tethering mWrapper; private final TetheringNotificationUpdater mNotificationUpdater; public boolean mDisallowTethering; public UserRestrictionActionListener(@NonNull UserManager um, @NonNull Tethering wrapper, @NonNull TetheringNotificationUpdater updater) { - mUserManager = um; + mUserMgr = um; mWrapper = wrapper; mNotificationUpdater = updater; mDisallowTethering = false; @@ -1037,7 +1041,7 @@ public class Tethering { // getUserRestrictions gets restriction for this process' user, which is the primary // user. This is fine because DISALLOW_CONFIG_TETHERING can only be set on the primary // user. See UserManager.DISALLOW_CONFIG_TETHERING. - final Bundle restrictions = mUserManager.getUserRestrictions(); + final Bundle restrictions = mUserMgr.getUserRestrictions(); final boolean newlyDisallowed = restrictions.getBoolean(UserManager.DISALLOW_CONFIG_TETHERING); final boolean prevDisallowed = mDisallowTethering; @@ -1988,7 +1992,7 @@ public class Tethering { mHandler.post(() -> { mTetheringEventCallbacks.register(callback, new CallbackCookie(hasListPermission)); final TetheringCallbackStartedParcel parcel = new TetheringCallbackStartedParcel(); - parcel.tetheringSupported = mDeps.isTetheringSupported(); + parcel.tetheringSupported = isTetheringSupported(); parcel.upstreamNetwork = mTetherUpstream; parcel.config = mConfig.toStableParcelable(); parcel.states = @@ -2111,6 +2115,20 @@ public class Tethering { } } + // if ro.tether.denied = true we default to no tethering + // gservices could set the secure setting to 1 though to enable it on a build where it + // had previously been turned off. + boolean isTetheringSupported() { + final int defaultVal = + SystemProperties.get("ro.tether.denied").equals("true") ? 0 : 1; + final boolean tetherSupported = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.TETHER_SUPPORTED, defaultVal) != 0; + final boolean tetherEnabledInSettings = tetherSupported + && !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING); + + return tetherEnabledInSettings && hasTetherableConfiguration(); + } + void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer, @Nullable String[] args) { // Binder.java closes the resource for us. @SuppressWarnings("resource") diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringService.java b/Tethering/src/com/android/networkstack/tethering/TetheringService.java index af349f2b92..7d01273842 100644 --- a/Tethering/src/com/android/networkstack/tethering/TetheringService.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringService.java @@ -40,15 +40,12 @@ import android.net.TetheringRequestParcel; import android.net.dhcp.DhcpServerCallbacks; import android.net.dhcp.DhcpServingParamsParcel; import android.net.ip.IpServer; -import android.net.util.SharedLog; import android.os.Binder; import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.os.ResultReceiver; -import android.os.SystemProperties; -import android.os.UserManager; import android.provider.Settings; import android.util.Log; @@ -68,21 +65,14 @@ import java.io.PrintWriter; public class TetheringService extends Service { private static final String TAG = TetheringService.class.getSimpleName(); - private final SharedLog mLog = new SharedLog(TAG); private TetheringConnector mConnector; - private Context mContext; - private TetheringDependencies mDeps; - private Tethering mTethering; - private UserManager mUserManager; @Override public void onCreate() { - mLog.mark("onCreate"); - mDeps = getTetheringDependencies(); - mContext = mDeps.getContext(); - mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - mTethering = makeTethering(mDeps); - mTethering.startStateMachineUpdaters(); + final TetheringDependencies deps = makeTetheringDependencies(); + // The Tethering object needs a fully functional context to start, so this can't be done + // in the constructor. + mConnector = new TetheringConnector(makeTethering(deps), TetheringService.this); } /** @@ -94,21 +84,10 @@ public class TetheringService extends Service { return new Tethering(deps); } - /** - * Create a binder connector for the system server to communicate with the tethering. - */ - private synchronized IBinder makeConnector() { - if (mConnector == null) { - mConnector = new TetheringConnector(mTethering, TetheringService.this); - } - return mConnector; - } - @NonNull @Override public IBinder onBind(Intent intent) { - mLog.mark("onBind"); - return makeConnector(); + return mConnector; } private static class TetheringConnector extends ITetheringConnector.Stub { @@ -248,7 +227,7 @@ public class TetheringService extends Service { listener.onResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); return true; } - if (!mService.isTetheringSupported()) { + if (!mTethering.isTetheringSupported()) { listener.onResult(TETHER_ERROR_UNSUPPORTED); return true; } @@ -266,7 +245,7 @@ public class TetheringService extends Service { receiver.send(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION, null); return true; } - if (!mService.isTetheringSupported()) { + if (!mTethering.isTetheringSupported()) { receiver.send(TETHER_ERROR_UNSUPPORTED, null); return true; } @@ -300,20 +279,6 @@ public class TetheringService extends Service { } } - // if ro.tether.denied = true we default to no tethering - // gservices could set the secure setting to 1 though to enable it on a build where it - // had previously been turned off. - private boolean isTetheringSupported() { - final int defaultVal = - SystemProperties.get("ro.tether.denied").equals("true") ? 0 : 1; - final boolean tetherSupported = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.TETHER_SUPPORTED, defaultVal) != 0; - final boolean tetherEnabledInSettings = tetherSupported - && !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING); - - return tetherEnabledInSettings && mTethering.hasTetherableConfiguration(); - } - /** * Check if the package is a allowed to write settings. This also accounts that such an access * happened. @@ -332,87 +297,79 @@ public class TetheringService extends Service { * An injection method for testing. */ @VisibleForTesting - public TetheringDependencies getTetheringDependencies() { - if (mDeps == null) { - mDeps = new TetheringDependencies() { - @Override - public NetworkRequest getDefaultNetworkRequest() { - // TODO: b/147280869, add a proper system API to replace this. - final NetworkRequest trackDefaultRequest = new NetworkRequest.Builder() - .clearCapabilities() - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) - .addCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED) - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) - .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) - .build(); - return trackDefaultRequest; - } + public TetheringDependencies makeTetheringDependencies() { + return new TetheringDependencies() { + @Override + public NetworkRequest getDefaultNetworkRequest() { + // TODO: b/147280869, add a proper system API to replace this. + final NetworkRequest trackDefaultRequest = new NetworkRequest.Builder() + .clearCapabilities() + .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) + .addCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED) + .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) + .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + .build(); + return trackDefaultRequest; + } - @Override - public Looper getTetheringLooper() { - final HandlerThread tetherThread = new HandlerThread("android.tethering"); - tetherThread.start(); - return tetherThread.getLooper(); - } + @Override + public Looper getTetheringLooper() { + final HandlerThread tetherThread = new HandlerThread("android.tethering"); + tetherThread.start(); + return tetherThread.getLooper(); + } - @Override - public boolean isTetheringSupported() { - return TetheringService.this.isTetheringSupported(); - } + @Override + public Context getContext() { + return TetheringService.this; + } - @Override - public Context getContext() { - return TetheringService.this; - } + @Override + public IpServer.Dependencies getIpServerDependencies() { + return new IpServer.Dependencies() { + @Override + public void makeDhcpServer(String ifName, DhcpServingParamsParcel params, + DhcpServerCallbacks cb) { + try { + final INetworkStackConnector service = getNetworkStackConnector(); + if (service == null) return; - @Override - public IpServer.Dependencies getIpServerDependencies() { - return new IpServer.Dependencies() { - @Override - public void makeDhcpServer(String ifName, DhcpServingParamsParcel params, - DhcpServerCallbacks cb) { + service.makeDhcpServer(ifName, params, cb); + } catch (RemoteException e) { + Log.e(TAG, "Fail to make dhcp server"); try { - final INetworkStackConnector service = getNetworkStackConnector(); - if (service == null) return; - - service.makeDhcpServer(ifName, params, cb); - } catch (RemoteException e) { - Log.e(TAG, "Fail to make dhcp server"); - try { - cb.onDhcpServerCreated(STATUS_UNKNOWN_ERROR, null); - } catch (RemoteException re) { } - } + cb.onDhcpServerCreated(STATUS_UNKNOWN_ERROR, null); + } catch (RemoteException re) { } } - }; - } - - // TODO: replace this by NetworkStackClient#getRemoteConnector after refactoring - // networkStackClient. - static final int NETWORKSTACK_TIMEOUT_MS = 60_000; - private INetworkStackConnector getNetworkStackConnector() { - IBinder connector; - try { - final long before = System.currentTimeMillis(); - while ((connector = NetworkStack.getService()) == null) { - if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) { - Log.wtf(TAG, "Timeout, fail to get INetworkStackConnector"); - return null; - } - Thread.sleep(200); - } - } catch (InterruptedException e) { - Log.wtf(TAG, "Interrupted, fail to get INetworkStackConnector"); - return null; } - return INetworkStackConnector.Stub.asInterface(connector); - } + }; + } - @Override - public BluetoothAdapter getBluetoothAdapter() { - return BluetoothAdapter.getDefaultAdapter(); + // TODO: replace this by NetworkStackClient#getRemoteConnector after refactoring + // networkStackClient. + static final int NETWORKSTACK_TIMEOUT_MS = 60_000; + private INetworkStackConnector getNetworkStackConnector() { + IBinder connector; + try { + final long before = System.currentTimeMillis(); + while ((connector = NetworkStack.getService()) == null) { + if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) { + Log.wtf(TAG, "Timeout, fail to get INetworkStackConnector"); + return null; + } + Thread.sleep(200); + } + } catch (InterruptedException e) { + Log.wtf(TAG, "Interrupted, fail to get INetworkStackConnector"); + return null; } - }; - } - return mDeps; + return INetworkStackConnector.Stub.asInterface(connector); + } + + @Override + public BluetoothAdapter getBluetoothAdapter() { + return BluetoothAdapter.getDefaultAdapter(); + } + }; } } diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java index 7df9fc6850..3dc6a1300d 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java @@ -83,8 +83,7 @@ public final class TetheringServiceTest { mTetheringConnector = mockConnector.getTetheringConnector(); final MockTetheringService service = mockConnector.getService(); mTethering = service.getTethering(); - verify(mTethering).startStateMachineUpdaters(); - when(mTethering.hasTetherableConfiguration()).thenReturn(true); + when(mTethering.isTetheringSupported()).thenReturn(true); } @After @@ -97,7 +96,7 @@ public final class TetheringServiceTest { when(mTethering.tether(TEST_IFACE_NAME)).thenReturn(TETHER_ERROR_NO_ERROR); final TestTetheringResult result = new TestTetheringResult(); mTetheringConnector.tether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result); - verify(mTethering).hasTetherableConfiguration(); + verify(mTethering).isTetheringSupported(); verify(mTethering).tether(TEST_IFACE_NAME); verifyNoMoreInteractions(mTethering); result.assertResult(TETHER_ERROR_NO_ERROR); @@ -109,7 +108,7 @@ public final class TetheringServiceTest { final TestTetheringResult result = new TestTetheringResult(); mTetheringConnector.untether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result); - verify(mTethering).hasTetherableConfiguration(); + verify(mTethering).isTetheringSupported(); verify(mTethering).untether(TEST_IFACE_NAME); verifyNoMoreInteractions(mTethering); result.assertResult(TETHER_ERROR_NO_ERROR); @@ -121,7 +120,7 @@ public final class TetheringServiceTest { final TestTetheringResult result = new TestTetheringResult(); mTetheringConnector.setUsbTethering(true /* enable */, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result); - verify(mTethering).hasTetherableConfiguration(); + verify(mTethering).isTetheringSupported(); verify(mTethering).setUsbTethering(true /* enable */); verifyNoMoreInteractions(mTethering); result.assertResult(TETHER_ERROR_NO_ERROR); @@ -133,7 +132,7 @@ public final class TetheringServiceTest { final TetheringRequestParcel request = new TetheringRequestParcel(); request.tetheringType = TETHERING_WIFI; mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result); - verify(mTethering).hasTetherableConfiguration(); + verify(mTethering).isTetheringSupported(); verify(mTethering).startTethering(eq(request), eq(result)); verifyNoMoreInteractions(mTethering); } @@ -143,7 +142,7 @@ public final class TetheringServiceTest { final TestTetheringResult result = new TestTetheringResult(); mTetheringConnector.stopTethering(TETHERING_WIFI, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result); - verify(mTethering).hasTetherableConfiguration(); + verify(mTethering).isTetheringSupported(); verify(mTethering).stopTethering(TETHERING_WIFI); verifyNoMoreInteractions(mTethering); result.assertResult(TETHER_ERROR_NO_ERROR); @@ -154,7 +153,7 @@ public final class TetheringServiceTest { final ResultReceiver result = new ResultReceiver(null); mTetheringConnector.requestLatestTetheringEntitlementResult(TETHERING_WIFI, result, true /* showEntitlementUi */, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG); - verify(mTethering).hasTetherableConfiguration(); + verify(mTethering).isTetheringSupported(); verify(mTethering).requestLatestTetheringEntitlementResult(eq(TETHERING_WIFI), eq(result), eq(true) /* showEntitlementUi */); verifyNoMoreInteractions(mTethering); @@ -181,7 +180,7 @@ public final class TetheringServiceTest { public void testStopAllTethering() throws Exception { final TestTetheringResult result = new TestTetheringResult(); mTetheringConnector.stopAllTethering(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result); - verify(mTethering).hasTetherableConfiguration(); + verify(mTethering).isTetheringSupported(); verify(mTethering).untetherAll(); verifyNoMoreInteractions(mTethering); result.assertResult(TETHER_ERROR_NO_ERROR); @@ -191,7 +190,7 @@ public final class TetheringServiceTest { public void testIsTetheringSupported() throws Exception { final TestTetheringResult result = new TestTetheringResult(); mTetheringConnector.isTetheringSupported(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result); - verify(mTethering).hasTetherableConfiguration(); + verify(mTethering).isTetheringSupported(); verifyNoMoreInteractions(mTethering); result.assertResult(TETHER_ERROR_NO_ERROR); } 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 2bd8ae0288..00174e6d8b 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java @@ -485,18 +485,6 @@ public class TetheringTest { MockitoAnnotations.initMocks(this); when(mResources.getStringArray(R.array.config_tether_dhcp_range)) .thenReturn(new String[0]); - when(mResources.getStringArray(R.array.config_tether_usb_regexs)) - .thenReturn(new String[] { "test_rndis\\d" }); - when(mResources.getStringArray(R.array.config_tether_wifi_regexs)) - .thenReturn(new String[]{ "test_wlan\\d" }); - when(mResources.getStringArray(R.array.config_tether_wifi_p2p_regexs)) - .thenReturn(new String[]{ "test_p2p-p2p\\d-.*" }); - when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs)) - .thenReturn(new String[0]); - when(mResources.getStringArray(R.array.config_tether_ncm_regexs)) - .thenReturn(new String[] { "test_ncm\\d" }); - when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[0]); - when(mResources.getBoolean(R.bool.config_tether_upstream_automatic)).thenReturn(false); when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( false); when(mNetd.interfaceGetList()) @@ -515,6 +503,7 @@ public class TetheringTest { mServiceContext = new TestContext(mContext); mContentResolver = new MockContentResolver(mServiceContext); mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); + setTetheringSupported(true /* supported */); mIntents = new Vector<>(); mBroadcastReceiver = new BroadcastReceiver() { @Override @@ -525,7 +514,6 @@ public class TetheringTest { mServiceContext.registerReceiver(mBroadcastReceiver, new IntentFilter(ACTION_TETHER_STATE_CHANGED)); mTethering = makeTethering(); - mTethering.startStateMachineUpdaters(); verify(mStatsManager, times(1)).registerNetworkStatsProvider(anyString(), any()); verify(mNetd).registerUnsolicitedEventListener(any()); final ArgumentCaptor phoneListenerCaptor = @@ -536,6 +524,31 @@ public class TetheringTest { mPhoneStateListener = phoneListenerCaptor.getValue(); } + private void setTetheringSupported(final boolean supported) { + Settings.Global.putInt(mContentResolver, Settings.Global.TETHER_SUPPORTED, + supported ? 1 : 0); + when(mUserManager.hasUserRestriction( + UserManager.DISALLOW_CONFIG_TETHERING)).thenReturn(!supported); + // Setup tetherable configuration. + when(mResources.getStringArray(R.array.config_tether_usb_regexs)) + .thenReturn(new String[] { "test_rndis\\d" }); + when(mResources.getStringArray(R.array.config_tether_wifi_regexs)) + .thenReturn(new String[]{ "test_wlan\\d" }); + when(mResources.getStringArray(R.array.config_tether_wifi_p2p_regexs)) + .thenReturn(new String[]{ "test_p2p-p2p\\d-.*" }); + when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs)) + .thenReturn(new String[0]); + when(mResources.getStringArray(R.array.config_tether_ncm_regexs)) + .thenReturn(new String[] { "test_ncm\\d" }); + when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[0]); + when(mResources.getBoolean(R.bool.config_tether_upstream_automatic)).thenReturn(true); + } + + private void initTetheringUpstream(UpstreamNetworkState upstreamState) { + when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState); + when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())).thenReturn(upstreamState); + } + private Tethering makeTethering() { mTetheringDependencies.reset(); return new Tethering(mTetheringDependencies); @@ -672,9 +685,7 @@ public class TetheringTest { } private void prepareUsbTethering(UpstreamNetworkState upstreamState) { - when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState); - when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())) - .thenReturn(upstreamState); + initTetheringUpstream(upstreamState); // Emulate pressing the USB tethering button in Settings UI. mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB), null); @@ -700,7 +711,7 @@ public class TetheringTest { verify(mNetd, times(1)).interfaceGetList(); // UpstreamNetworkMonitor should receive selected upstream - verify(mUpstreamNetworkMonitor, times(1)).selectPreferredUpstreamType(any()); + verify(mUpstreamNetworkMonitor, times(1)).getCurrentPreferredUpstream(); verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(upstreamState.network); } @@ -872,8 +883,7 @@ public class TetheringTest { // Then 464xlat comes up upstreamState = buildMobile464xlatUpstreamState(); - when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())) - .thenReturn(upstreamState); + initTetheringUpstream(upstreamState); // Upstream LinkProperties changed: UpstreamNetworkMonitor sends EVENT_ON_LINKPROPERTIES. mTetheringDependencies.mUpstreamNetworkMonitorMasterSM.sendMessage( @@ -1344,9 +1354,7 @@ public class TetheringTest { callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED); // 2. Enable wifi tethering. UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState(); - when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState); - when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())) - .thenReturn(upstreamState); + initTetheringUpstream(upstreamState); when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true); mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true); mLooper.dispatchAll(); @@ -1723,7 +1731,7 @@ public class TetheringTest { final Tethering.TetherMasterSM stateMachine = (Tethering.TetherMasterSM) mTetheringDependencies.mUpstreamNetworkMonitorMasterSM; final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState(); - when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())).thenReturn(upstreamState); + initTetheringUpstream(upstreamState); stateMachine.chooseUpstreamType(true); verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(eq(upstreamState.network)); @@ -1735,7 +1743,7 @@ public class TetheringTest { final Tethering.TetherMasterSM stateMachine = (Tethering.TetherMasterSM) mTetheringDependencies.mUpstreamNetworkMonitorMasterSM; final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState(); - when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())).thenReturn(upstreamState); + initTetheringUpstream(upstreamState); stateMachine.chooseUpstreamType(true); stateMachine.handleUpstreamNetworkMonitorCallback(EVENT_ON_CAPABILITIES, upstreamState); From 91b2bda6829b4cc2fe4a4bf25bda20e0b7baca0b Mon Sep 17 00:00:00 2001 From: Treehugger Robot Date: Thu, 14 May 2020 16:33:30 +0000 Subject: [PATCH 141/188] Fix READ_DEVICE_CONFIG permission denied problem in TetheringTests Bug: 156557122 Test: atest TetheringTests Merged-In: Id9c0397306f3872fc23520d1354f338035a96dc9 Change-Id: Id9c0397306f3872fc23520d1354f338035a96dc9 --- .../tethering/TetheringConfiguration.java | 33 ++++++++++--------- .../tethering/EntitlementManagerTest.java | 5 ++- .../tethering/TetheringConfigurationTest.java | 25 +++++++------- .../networkstack/tethering/TetheringTest.java | 4 +-- 4 files changed, 33 insertions(+), 34 deletions(-) diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java index 91a6e29a05..48a600dfe6 100644 --- a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java @@ -132,7 +132,7 @@ public class TetheringConfiguration { isDunRequired = checkDunRequired(ctx); chooseUpstreamAutomatically = getResourceBoolean( - res, R.bool.config_tether_upstream_automatic, false /** default value */); + res, R.bool.config_tether_upstream_automatic, false /** defaultValue */); preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(res, isDunRequired); legacyDhcpRanges = getLegacyDhcpRanges(res); @@ -375,30 +375,31 @@ public class TetheringConfiguration { // Priority 1: Device config // Priority 2: Resource config // Priority 3: Default value - final boolean resourceValue = getResourceBoolean( + final boolean defaultValue = getResourceBoolean( res, R.bool.config_tether_enable_bpf_offload, true /** default value */); - // Due to the limitation of static mock for testing, using #getProperty directly instead - // of getDeviceConfigBoolean. getDeviceConfigBoolean is not invoked because it uses - // #getBoolean to get the boolean device config. The test can't know that the returned - // boolean value comes from device config or default value (because of null property - // string). Because the test would like to verify null property boolean string case, - // use DeviceConfig.getProperty here. See also the test case testBpfOffload{*} in - // TetheringConfigurationTest.java. - final String value = DeviceConfig.getProperty( - NAMESPACE_CONNECTIVITY, OVERRIDE_TETHER_ENABLE_BPF_OFFLOAD); - return (value != null) ? Boolean.parseBoolean(value) : resourceValue; + return getDeviceConfigBoolean(OVERRIDE_TETHER_ENABLE_BPF_OFFLOAD, defaultValue); } private boolean getEnableLegacyDhcpServer(final Resources res) { return getResourceBoolean( - res, R.bool.config_tether_enable_legacy_dhcp_server, false /** default value */) - || getDeviceConfigBoolean(TETHER_ENABLE_LEGACY_DHCP_SERVER); + res, R.bool.config_tether_enable_legacy_dhcp_server, false /** defaultValue */) + || getDeviceConfigBoolean( + TETHER_ENABLE_LEGACY_DHCP_SERVER, false /** defaultValue */); + } + + private boolean getDeviceConfigBoolean(final String name, final boolean defaultValue) { + // Due to the limitation of static mock for testing, using #getDeviceConfigProperty instead + // of DeviceConfig#getBoolean. If using #getBoolean here, the test can't know that the + // returned boolean value comes from device config or default value (because of null + // property string). See the test case testBpfOffload{*} in TetheringConfigurationTest.java. + final String value = getDeviceConfigProperty(name); + return value != null ? Boolean.parseBoolean(value) : defaultValue; } @VisibleForTesting - protected boolean getDeviceConfigBoolean(final String name) { - return DeviceConfig.getBoolean(NAMESPACE_CONNECTIVITY, name, false /** defaultValue */); + protected String getDeviceConfigProperty(String name) { + return DeviceConfig.getProperty(NAMESPACE_CONNECTIVITY, name); } private Resources getResources(Context ctx, int subId) { diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java index cdd0e243e3..72fa916b9e 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java @@ -147,9 +147,8 @@ public final class EntitlementManagerTest { doReturn(false).when( () -> SystemProperties.getBoolean( eq(EntitlementManager.DISABLE_PROVISIONING_SYSPROP_KEY), anyBoolean())); - doReturn(false).when( - () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY), - eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean())); + doReturn(null).when( + () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY), anyString())); when(mResources.getStringArray(R.array.config_tether_dhcp_range)) .thenReturn(new String[0]); diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java index fbfa871f76..1999ad786e 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java @@ -30,7 +30,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSess import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.when; @@ -109,9 +108,9 @@ public class TetheringConfigurationTest { .mockStatic(DeviceConfig.class) .strictness(Strictness.WARN) .startMocking(); - doReturn(false).when( - () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY), - eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean())); + doReturn(null).when( + () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY), + eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER))); when(mResources.getStringArray(R.array.config_tether_dhcp_range)).thenReturn( new String[0]); @@ -328,9 +327,9 @@ public class TetheringConfigurationTest { public void testNewDhcpServerDisabled() { when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( true); - doReturn(false).when( - () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY), - eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean())); + doReturn("false").when( + () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY), + eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER))); final TetheringConfiguration enableByRes = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); @@ -338,9 +337,9 @@ public class TetheringConfigurationTest { when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( false); - doReturn(true).when( - () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY), - eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean())); + doReturn("true").when( + () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY), + eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER))); final TetheringConfiguration enableByDevConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); @@ -351,9 +350,9 @@ public class TetheringConfigurationTest { public void testNewDhcpServerEnabled() { when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( false); - doReturn(false).when( - () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY), - eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean())); + doReturn("false").when( + () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY), + eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER))); final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); 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 00174e6d8b..0132aba0b7 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java @@ -312,8 +312,8 @@ public class TetheringTest { } @Override - protected boolean getDeviceConfigBoolean(final String name) { - return false; + protected String getDeviceConfigProperty(final String name) { + return null; } @Override From 654013479b24a7b976c01249c961b3cfde2eb577 Mon Sep 17 00:00:00 2001 From: Xiao Ma Date: Thu, 14 May 2020 06:52:59 +0000 Subject: [PATCH 142/188] Support MirrorLink DHCPDECLINE. Add the specific implementation of onNewPrefixRequest callback on IpServer side, also refactor some common code. Bug: 130741856 Test: atest TetheringTests Merged-In: If2871bf899cb5890bbfee18063a194c92b6f474e Change-Id: If2871bf899cb5890bbfee18063a194c92b6f474e --- .../net/dhcp/DhcpServingParamsParcelExt.java | 12 + Tethering/src/android/net/ip/IpServer.java | 232 +++++++++++++----- .../unit/src/android/net/ip/IpServerTest.java | 78 +++++- 3 files changed, 253 insertions(+), 69 deletions(-) diff --git a/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java b/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java index 4f8ad8a221..4710a30b29 100644 --- a/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java +++ b/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java @@ -173,6 +173,18 @@ public class DhcpServingParamsParcelExt extends DhcpServingParamsParcel { return this; } + /** + * Set whether the DHCP server should request a new prefix from IpServer when receiving + * DHCPDECLINE message in certain particular link (e.g. there is only one downstream USB + * tethering client). If it's false, process DHCPDECLINE message as RFC2131#4.3.3 suggests. + * + *

If not set, the default value is false. + */ + public DhcpServingParamsParcelExt setChangePrefixOnDecline(boolean changePrefixOnDecline) { + this.changePrefixOnDecline = changePrefixOnDecline; + return this; + } + private static int[] toIntArray(@NonNull Collection addrs) { int[] res = new int[addrs.size()]; int i = 0; diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java index d993306b17..de537871a7 100644 --- a/Tethering/src/android/net/ip/IpServer.java +++ b/Tethering/src/android/net/ip/IpServer.java @@ -50,6 +50,7 @@ import android.net.shared.NetdUtils; import android.net.shared.RouteUtils; import android.net.util.InterfaceParams; import android.net.util.InterfaceSet; +import android.net.util.PrefixUtils; import android.net.util.SharedLog; import android.os.Handler; import android.os.Looper; @@ -60,6 +61,7 @@ import android.util.Log; import android.util.SparseArray; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.android.internal.util.MessageUtils; import com.android.internal.util.State; @@ -115,6 +117,15 @@ public class IpServer extends StateMachine { private static final String ETHERNET_IFACE_ADDR = "192.168.50.1"; private static final int ETHERNET_IFACE_PREFIX_LENGTH = 24; + // TODO: remove this constant after introducing PrivateAddressCoordinator. + private static final List NCM_PREFIXES = Collections.unmodifiableList( + Arrays.asList( + new IpPrefix("192.168.42.0/24"), + new IpPrefix("192.168.51.0/24"), + new IpPrefix("192.168.52.0/24"), + new IpPrefix("192.168.53.0/24") + )); + // TODO: have PanService use some visible version of this constant private static final String BLUETOOTH_IFACE_ADDR = "192.168.44.1"; private static final int BLUETOOTH_DHCP_PREFIX_LENGTH = 24; @@ -212,6 +223,8 @@ public class IpServer extends StateMachine { public static final int CMD_IPV6_TETHER_UPDATE = BASE_IPSERVER + 10; // new neighbor cache entry on our interface public static final int CMD_NEIGHBOR_EVENT = BASE_IPSERVER + 11; + // request from DHCP server that it wants to have a new prefix + public static final int CMD_NEW_PREFIX_REQUEST = BASE_IPSERVER + 12; private final State mInitialState; private final State mLocalHotspotState; @@ -462,7 +475,7 @@ public class IpServer extends StateMachine { handleError(); } } - }, new DhcpLeaseCallback()); + }, new DhcpEventCallback()); } catch (RemoteException e) { throw new IllegalStateException(e); } @@ -475,7 +488,7 @@ public class IpServer extends StateMachine { } } - private class DhcpLeaseCallback extends IDhcpEventCallbacks.Stub { + private class DhcpEventCallback extends IDhcpEventCallbacks.Stub { @Override public void onLeasesChanged(List leaseParcelables) { final ArrayList leases = new ArrayList<>(); @@ -509,8 +522,9 @@ public class IpServer extends StateMachine { } @Override - public void onNewPrefixRequest(IpPrefix currentPrefix) { - //TODO: add specific implementation. + public void onNewPrefixRequest(@NonNull final IpPrefix currentPrefix) { + Objects.requireNonNull(currentPrefix); + sendMessage(CMD_NEW_PREFIX_REQUEST, currentPrefix); } @Override @@ -524,26 +538,38 @@ public class IpServer extends StateMachine { } } + private RouteInfo getDirectConnectedRoute(@NonNull final LinkAddress ipv4Address) { + Objects.requireNonNull(ipv4Address); + return new RouteInfo(PrefixUtils.asIpPrefix(ipv4Address), null, mIfaceName, RTN_UNICAST); + } + + private DhcpServingParamsParcel makeServingParams(@NonNull final Inet4Address defaultRouter, + @NonNull final Inet4Address dnsServer, @NonNull LinkAddress serverAddr, + @Nullable Inet4Address clientAddr) { + final boolean changePrefixOnDecline = + (mInterfaceType == TetheringManager.TETHERING_NCM && clientAddr == null); + return new DhcpServingParamsParcelExt() + .setDefaultRouters(defaultRouter) + .setDhcpLeaseTimeSecs(DHCP_LEASE_TIME_SECS) + .setDnsServers(dnsServer) + .setServerAddr(serverAddr) + .setMetered(true) + .setSingleClientAddr(clientAddr) + .setChangePrefixOnDecline(changePrefixOnDecline); + // TODO: also advertise link MTU + } + private boolean startDhcp(final LinkAddress serverLinkAddr, final LinkAddress clientLinkAddr) { if (mUsingLegacyDhcp) { return true; } final Inet4Address addr = (Inet4Address) serverLinkAddr.getAddress(); - final int prefixLen = serverLinkAddr.getPrefixLength(); final Inet4Address clientAddr = clientLinkAddr == null ? null : (Inet4Address) clientLinkAddr.getAddress(); - final DhcpServingParamsParcel params; - params = new DhcpServingParamsParcelExt() - .setDefaultRouters(addr) - .setDhcpLeaseTimeSecs(DHCP_LEASE_TIME_SECS) - .setDnsServers(addr) - .setServerAddr(serverLinkAddr) - .setMetered(true) - .setSingleClientAddr(clientAddr); - // TODO: also advertise link MTU - + final DhcpServingParamsParcel params = makeServingParams(addr /* defaultRouter */, + addr /* dnsServer */, serverLinkAddr, clientAddr); mDhcpServerStartIndex++; mDeps.makeDhcpServer( mIfaceName, params, new DhcpServerCallbacksImpl(mDhcpServerStartIndex)); @@ -570,7 +596,7 @@ public class IpServer extends StateMachine { }); mDhcpServer = null; } catch (RemoteException e) { - mLog.e("Error stopping DHCP", e); + mLog.e("Error stopping DHCP server", e); // Not much more we can do here } } @@ -652,31 +678,33 @@ public class IpServer extends StateMachine { return false; } - // Directly-connected route. - final IpPrefix ipv4Prefix = new IpPrefix(mIpv4Address.getAddress(), - mIpv4Address.getPrefixLength()); - final RouteInfo route = new RouteInfo(ipv4Prefix, null, null, RTN_UNICAST); if (enabled) { mLinkProperties.addLinkAddress(mIpv4Address); - mLinkProperties.addRoute(route); + mLinkProperties.addRoute(getDirectConnectedRoute(mIpv4Address)); } else { mLinkProperties.removeLinkAddress(mIpv4Address); - mLinkProperties.removeRoute(route); + mLinkProperties.removeRoute(getDirectConnectedRoute(mIpv4Address)); } - return configureDhcp(enabled, mIpv4Address, mStaticIpv4ClientAddr); } - private String getRandomWifiIPv4Address() { + private Inet4Address getRandomIPv4Address(@NonNull final byte[] rawAddr) { + final byte[] ipv4Addr = rawAddr; + ipv4Addr[3] = getRandomSanitizedByte(DOUG_ADAMS, asByte(0), asByte(1), FF); try { - byte[] bytes = parseNumericAddress(WIFI_HOST_IFACE_ADDR).getAddress(); - bytes[3] = getRandomSanitizedByte(DOUG_ADAMS, asByte(0), asByte(1), FF); - return InetAddress.getByAddress(bytes).getHostAddress(); - } catch (Exception e) { - return WIFI_HOST_IFACE_ADDR; + return (Inet4Address) InetAddress.getByAddress(ipv4Addr); + } catch (UnknownHostException e) { + mLog.e("Failed to construct Inet4Address from raw IPv4 addr"); + return null; } } + private String getRandomWifiIPv4Address() { + final Inet4Address ipv4Addr = + getRandomIPv4Address(parseNumericAddress(WIFI_HOST_IFACE_ADDR).getAddress()); + return ipv4Addr != null ? ipv4Addr.getHostAddress() : WIFI_HOST_IFACE_ADDR; + } + private boolean startIPv6() { mInterfaceParams = mDeps.getInterfaceParams(mIfaceName); if (mInterfaceParams == null) { @@ -761,21 +789,43 @@ public class IpServer extends StateMachine { mLastIPv6UpstreamIfindex = upstreamIfindex; } + private void removeRoutesFromLocalNetwork(@NonNull final List toBeRemoved) { + final int removalFailures = RouteUtils.removeRoutesFromLocalNetwork( + mNetd, toBeRemoved); + if (removalFailures > 0) { + mLog.e(String.format("Failed to remove %d IPv6 routes from local table.", + removalFailures)); + } + + for (RouteInfo route : toBeRemoved) mLinkProperties.removeRoute(route); + } + + private void addRoutesToLocalNetwork(@NonNull final List toBeAdded) { + try { + // It's safe to call networkAddInterface() even if + // the interface is already in the local_network. + mNetd.networkAddInterface(INetd.LOCAL_NET_ID, mIfaceName); + try { + // Add routes from local network. Note that adding routes that + // already exist does not cause an error (EEXIST is silently ignored). + RouteUtils.addRoutesToLocalNetwork(mNetd, mIfaceName, toBeAdded); + } catch (IllegalStateException e) { + mLog.e("Failed to add IPv4/v6 routes to local table: " + e); + return; + } + } catch (ServiceSpecificException | RemoteException e) { + mLog.e("Failed to add " + mIfaceName + " to local table: ", e); + return; + } + + for (RouteInfo route : toBeAdded) mLinkProperties.addRoute(route); + } + private void configureLocalIPv6Routes( HashSet deprecatedPrefixes, HashSet newPrefixes) { // [1] Remove the routes that are deprecated. if (!deprecatedPrefixes.isEmpty()) { - final ArrayList toBeRemoved = - getLocalRoutesFor(mIfaceName, deprecatedPrefixes); - // Remove routes from local network. - final int removalFailures = RouteUtils.removeRoutesFromLocalNetwork( - mNetd, toBeRemoved); - if (removalFailures > 0) { - mLog.e(String.format("Failed to remove %d IPv6 routes from local table.", - removalFailures)); - } - - for (RouteInfo route : toBeRemoved) mLinkProperties.removeRoute(route); + removeRoutesFromLocalNetwork(getLocalRoutesFor(mIfaceName, deprecatedPrefixes)); } // [2] Add only the routes that have not previously been added. @@ -786,24 +836,7 @@ public class IpServer extends StateMachine { } if (!addedPrefixes.isEmpty()) { - final ArrayList toBeAdded = - getLocalRoutesFor(mIfaceName, addedPrefixes); - try { - // It's safe to call networkAddInterface() even if - // the interface is already in the local_network. - mNetd.networkAddInterface(INetd.LOCAL_NET_ID, mIfaceName); - try { - // Add routes from local network. Note that adding routes that - // already exist does not cause an error (EEXIST is silently ignored). - RouteUtils.addRoutesToLocalNetwork(mNetd, mIfaceName, toBeAdded); - } catch (IllegalStateException e) { - mLog.e("Failed to add IPv6 routes to local table: " + e); - } - } catch (ServiceSpecificException | RemoteException e) { - mLog.e("Failed to add " + mIfaceName + " to local table: ", e); - } - - for (RouteInfo route : toBeAdded) mLinkProperties.addRoute(route); + addRoutesToLocalNetwork(getLocalRoutesFor(mIfaceName, addedPrefixes)); } } } @@ -945,6 +978,80 @@ public class IpServer extends StateMachine { } } + // TODO: call PrivateAddressCoordinator.requestDownstreamAddress instead of this temporary + // logic. + private Inet4Address requestDownstreamAddress(@NonNull final IpPrefix currentPrefix) { + final int oldIndex = NCM_PREFIXES.indexOf(currentPrefix); + if (oldIndex == -1) { + mLog.e("current prefix isn't supported for NCM link: " + currentPrefix); + return null; + } + + final IpPrefix newPrefix = NCM_PREFIXES.get((oldIndex + 1) % NCM_PREFIXES.size()); + return getRandomIPv4Address(newPrefix.getRawAddress()); + } + + private void handleNewPrefixRequest(@NonNull final IpPrefix currentPrefix) { + if (!currentPrefix.contains(mIpv4Address.getAddress()) + || currentPrefix.getPrefixLength() != mIpv4Address.getPrefixLength()) { + Log.e(TAG, "Invalid prefix: " + currentPrefix); + return; + } + + final LinkAddress deprecatedLinkAddress = mIpv4Address; + final Inet4Address srvAddr = requestDownstreamAddress(currentPrefix); + if (srvAddr == null) { + mLog.e("Fail to request a new downstream prefix"); + return; + } + mIpv4Address = new LinkAddress(srvAddr, currentPrefix.getPrefixLength()); + + // Add new IPv4 address on the interface. + if (!mInterfaceCtrl.addAddress(srvAddr, currentPrefix.getPrefixLength())) { + mLog.e("Failed to add new IP " + srvAddr); + return; + } + + // Remove deprecated routes from local network. + removeRoutesFromLocalNetwork( + Collections.singletonList(getDirectConnectedRoute(deprecatedLinkAddress))); + mLinkProperties.removeLinkAddress(deprecatedLinkAddress); + + // Add new routes to local network. + addRoutesToLocalNetwork( + Collections.singletonList(getDirectConnectedRoute(mIpv4Address))); + mLinkProperties.addLinkAddress(mIpv4Address); + + // Update local DNS caching server with new IPv4 address, otherwise, dnsmasq doesn't + // listen on the interface configured with new IPv4 address, that results DNS validation + // failure of downstream client even if appropriate routes have been configured. + try { + mNetd.tetherApplyDnsInterfaces(); + } catch (ServiceSpecificException | RemoteException e) { + mLog.e("Failed to update local DNS caching server"); + return; + } + sendLinkProperties(); + + // Notify DHCP server that new prefix/route has been applied on IpServer. + final Inet4Address clientAddr = mStaticIpv4ClientAddr == null ? null : + (Inet4Address) mStaticIpv4ClientAddr.getAddress(); + final DhcpServingParamsParcel params = makeServingParams(srvAddr /* defaultRouter */, + srvAddr /* dnsServer */, mIpv4Address /* serverLinkAddress */, clientAddr); + try { + mDhcpServer.updateParams(params, new OnHandlerStatusCallback() { + @Override + public void callback(int statusCode) { + if (statusCode != STATUS_SUCCESS) { + mLog.e("Error updating DHCP serving params: " + statusCode); + } + } + }); + } catch (RemoteException e) { + mLog.e("Error updating DHCP serving params", e); + } + } + private byte getHopLimit(String upstreamIface) { try { int upstreamHopLimit = Integer.parseUnsignedInt( @@ -1056,11 +1163,9 @@ public class IpServer extends StateMachine { } try { - final IpPrefix ipv4Prefix = new IpPrefix(mIpv4Address.getAddress(), - mIpv4Address.getPrefixLength()); - NetdUtils.tetherInterface(mNetd, mIfaceName, ipv4Prefix); + NetdUtils.tetherInterface(mNetd, mIfaceName, PrefixUtils.asIpPrefix(mIpv4Address)); } catch (RemoteException | ServiceSpecificException | IllegalStateException e) { - mLog.e("Error Tethering: " + e); + mLog.e("Error Tethering", e); mLastError = TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR; return; } @@ -1115,6 +1220,9 @@ public class IpServer extends StateMachine { mLastError = TetheringManager.TETHER_ERROR_INTERNAL_ERROR; transitionTo(mInitialState); break; + case CMD_NEW_PREFIX_REQUEST: + handleNewPrefixRequest((IpPrefix) message.obj); + break; default: return false; } diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java index b9622da9d2..cd1ff607b4 100644 --- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java +++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java @@ -18,6 +18,7 @@ package android.net.ip; import static android.net.INetd.IF_STATE_UP; import static android.net.TetheringManager.TETHERING_BLUETOOTH; +import static android.net.TetheringManager.TETHERING_NCM; import static android.net.TetheringManager.TETHERING_USB; import static android.net.TetheringManager.TETHERING_WIFI; import static android.net.TetheringManager.TETHERING_WIFI_P2P; @@ -38,6 +39,7 @@ import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -67,6 +69,7 @@ import android.net.MacAddress; import android.net.RouteInfo; import android.net.TetherOffloadRuleParcel; import android.net.dhcp.DhcpServingParamsParcel; +import android.net.dhcp.IDhcpEventCallbacks; import android.net.dhcp.IDhcpServer; import android.net.dhcp.IDhcpServerCallbacks; import android.net.ip.IpNeighborMonitor.NeighborEvent; @@ -94,6 +97,7 @@ import org.mockito.MockitoAnnotations; import java.net.Inet4Address; import java.net.InetAddress; import java.util.Arrays; +import java.util.List; @RunWith(AndroidJUnit4.class) @SmallTest @@ -496,6 +500,59 @@ public class IpServerTest { assertDhcpStarted(new IpPrefix("192.168.49.0/24")); } + @Test + public void startsDhcpServerOnNcm() throws Exception { + initStateMachine(TETHERING_NCM); + dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_LOCAL_ONLY); + dispatchTetherConnectionChanged(UPSTREAM_IFACE); + + assertDhcpStarted(new IpPrefix("192.168.42.0/24")); + } + + @Test + public void testOnNewPrefixRequest() throws Exception { + initStateMachine(TETHERING_NCM); + dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_LOCAL_ONLY); + + final IDhcpEventCallbacks eventCallbacks; + final ArgumentCaptor dhcpEventCbsCaptor = + ArgumentCaptor.forClass(IDhcpEventCallbacks.class); + verify(mDhcpServer, timeout(MAKE_DHCPSERVER_TIMEOUT_MS).times(1)).startWithCallbacks( + any(), dhcpEventCbsCaptor.capture()); + eventCallbacks = dhcpEventCbsCaptor.getValue(); + assertDhcpStarted(new IpPrefix("192.168.42.0/24")); + + // Simulate the DHCP server receives DHCPDECLINE on MirrorLink and then signals + // onNewPrefixRequest callback. + eventCallbacks.onNewPrefixRequest(new IpPrefix("192.168.42.0/24")); + mLooper.dispatchAll(); + + final ArgumentCaptor lpCaptor = + ArgumentCaptor.forClass(LinkProperties.class); + InOrder inOrder = inOrder(mNetd, mCallback); + inOrder.verify(mCallback).updateInterfaceState( + mIpServer, STATE_LOCAL_ONLY, TETHER_ERROR_NO_ERROR); + inOrder.verify(mCallback).updateLinkProperties(eq(mIpServer), lpCaptor.capture()); + inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME); + // One for ipv4 route, one for ipv6 link local route. + inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME), + any(), any()); + inOrder.verify(mNetd).tetherApplyDnsInterfaces(); + inOrder.verify(mCallback).updateLinkProperties(eq(mIpServer), lpCaptor.capture()); + verifyNoMoreInteractions(mCallback); + + final LinkProperties linkProperties = lpCaptor.getValue(); + final List linkAddresses = linkProperties.getLinkAddresses(); + assertEquals(1, linkProperties.getLinkAddresses().size()); + assertEquals(1, linkProperties.getRoutes().size()); + final IpPrefix prefix = new IpPrefix(linkAddresses.get(0).getAddress(), + linkAddresses.get(0).getPrefixLength()); + assertNotEquals(prefix, new IpPrefix("192.168.42.0/24")); + + verify(mDhcpServer).updateParams(mDhcpParamsCaptor.capture(), any()); + assertDhcpServingParams(mDhcpParamsCaptor.getValue(), prefix); + } + @Test public void doesNotStartDhcpServerIfDisabled() throws Exception { initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, true /* usingLegacyDhcp */, @@ -731,19 +788,26 @@ public class IpServerTest { verify(mIpNeighborMonitor, never()).start(); } - private void assertDhcpStarted(IpPrefix expectedPrefix) throws Exception { - verify(mDependencies, times(1)).makeDhcpServer(eq(IFACE_NAME), any(), any()); - verify(mDhcpServer, timeout(MAKE_DHCPSERVER_TIMEOUT_MS).times(1)).startWithCallbacks( - any(), any()); - final DhcpServingParamsParcel params = mDhcpParamsCaptor.getValue(); + private void assertDhcpServingParams(final DhcpServingParamsParcel params, + final IpPrefix prefix) { // Last address byte is random - assertTrue(expectedPrefix.contains(intToInet4AddressHTH(params.serverAddr))); - assertEquals(expectedPrefix.getPrefixLength(), params.serverAddrPrefixLength); + assertTrue(prefix.contains(intToInet4AddressHTH(params.serverAddr))); + assertEquals(prefix.getPrefixLength(), params.serverAddrPrefixLength); assertEquals(1, params.defaultRouters.length); assertEquals(params.serverAddr, params.defaultRouters[0]); assertEquals(1, params.dnsServers.length); assertEquals(params.serverAddr, params.dnsServers[0]); assertEquals(DHCP_LEASE_TIME_SECS, params.dhcpLeaseTimeSecs); + if (mIpServer.interfaceType() == TETHERING_NCM) { + assertTrue(params.changePrefixOnDecline); + } + } + + private void assertDhcpStarted(IpPrefix expectedPrefix) throws Exception { + verify(mDependencies, times(1)).makeDhcpServer(eq(IFACE_NAME), any(), any()); + verify(mDhcpServer, timeout(MAKE_DHCPSERVER_TIMEOUT_MS).times(1)).startWithCallbacks( + any(), any()); + assertDhcpServingParams(mDhcpParamsCaptor.getValue(), expectedPrefix); } /** From 5232658de2e83b9ecb218ed355f338311a980442 Mon Sep 17 00:00:00 2001 From: markchien Date: Fri, 17 Apr 2020 16:18:44 +0800 Subject: [PATCH 143/188] TetheringServiceTest: test caller permission Bug: 154869719 Test: atest TetheringTests Original-Change: https://android-review.googlesource.com/1288503 Fix TetheringServiceTest test WRITE_SETTINGS permission failure AdoptShellPermissionIdentity can not pass permission check by Settings#checkAndNoteWriteSettingsOperation. It would compare the caller uid and its package name. See error below: 1. java.lang.SecurityException: Specified package com.android.shell under uid 10239 but it is really 2000 2. java.lang.SecurityException: uid 10245 does not have android.permission.UPDATE_APP_OPS_STATS. Override the method and test if caller hold WRITE_SETTINGS directly. Bug: 154869719 Test: TetheringTests, TetheringCoverageTests, NetworkStackNextTests, NetworkStackCoverageTests Original-Change: https://android-review.googlesource.com/1313806 Change-Id: I7beea3f011d930e433443ed62d772a3f8cce5d78 Merged-In: I7beea3f011d930e433443ed62d772a3f8cce5d78 --- .../tethering/TetheringService.java | 17 +- Tethering/tests/unit/AndroidManifest.xml | 3 - .../tethering/MockTetheringService.java | 14 + .../tethering/TetheringServiceTest.java | 359 +++++++++++++++--- 4 files changed, 343 insertions(+), 50 deletions(-) diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringService.java b/Tethering/src/com/android/networkstack/tethering/TetheringService.java index bf7fb042ed..e095afea52 100644 --- a/Tethering/src/com/android/networkstack/tethering/TetheringService.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringService.java @@ -254,8 +254,8 @@ public class TetheringService extends Service { // If callerPkg's uid is not same as Binder.getCallingUid(), // checkAndNoteWriteSettingsOperation will return false and the operation will be // denied. - return Settings.checkAndNoteWriteSettingsOperation(mService, uid, callerPkg, - false /* throwException */); + return mService.checkAndNoteWriteSettingsOperation(mService, uid, callerPkg, + false /* throwException */); } private boolean hasTetherAccessPermission() { @@ -266,6 +266,19 @@ public class TetheringService extends Service { } } + /** + * Check if the package is a allowed to write settings. This also accounts that such an access + * happened. + * + * @return {@code true} iff the package is allowed to write settings. + */ + @VisibleForTesting + boolean checkAndNoteWriteSettingsOperation(@NonNull Context context, int uid, + @NonNull String callingPackage, boolean throwException) { + return Settings.checkAndNoteWriteSettingsOperation(context, uid, callingPackage, + throwException); + } + /** * An injection method for testing. */ diff --git a/Tethering/tests/unit/AndroidManifest.xml b/Tethering/tests/unit/AndroidManifest.xml index 31eaabff52..355342f643 100644 --- a/Tethering/tests/unit/AndroidManifest.xml +++ b/Tethering/tests/unit/AndroidManifest.xml @@ -16,9 +16,6 @@ - - - 0) mUiAutomation.adoptShellPermissionIdentity(permissions); + try { + when(mTethering.isTetheringSupported()).thenReturn(true); + test.runTetheringCall(new TestTetheringResult()); + } finally { + mUiAutomation.dropShellPermissionIdentity(); + } + } + + private void verifyNoMoreInteractionsForTethering() { + verifyNoMoreInteractions(mTethering); + verifyNoMoreInteractions(mITetheringEventCallback); + reset(mTethering, mITetheringEventCallback); + } + + private void runTether(final TestTetheringResult result) throws Exception { + when(mTethering.tether(TEST_IFACE_NAME)).thenReturn(TETHER_ERROR_NO_ERROR); + mTetheringConnector.tether(TEST_IFACE_NAME, TEST_CALLER_PKG, result); + verify(mTethering).isTetheringSupported(); + verify(mTethering).tether(TEST_IFACE_NAME); + result.assertResult(TETHER_ERROR_NO_ERROR); } @Test public void testTether() throws Exception { - when(mTethering.tether(TEST_IFACE_NAME)).thenReturn(TETHER_ERROR_NO_ERROR); - final TestTetheringResult result = new TestTetheringResult(); - mTetheringConnector.tether(TEST_IFACE_NAME, TEST_CALLER_PKG, result); + runAsNoPermission((result) -> { + mTetheringConnector.tether(TEST_IFACE_NAME, TEST_CALLER_PKG, result); + verify(mTethering).isTetherProvisioningRequired(); + result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); + verifyNoMoreInteractionsForTethering(); + }); + + runAsTetherPrivileged((result) -> { + runTether(result); + verifyNoMoreInteractionsForTethering(); + }); + + runAsWriteSettings((result) -> { + runTether(result); + verify(mTethering).isTetherProvisioningRequired(); + verifyNoMoreInteractionsForTethering(); + }); + } + + private void runUnTether(final TestTetheringResult result) throws Exception { + when(mTethering.untether(TEST_IFACE_NAME)).thenReturn(TETHER_ERROR_NO_ERROR); + mTetheringConnector.untether(TEST_IFACE_NAME, TEST_CALLER_PKG, result); verify(mTethering).isTetheringSupported(); - verify(mTethering).tether(TEST_IFACE_NAME); - verifyNoMoreInteractions(mTethering); + verify(mTethering).untether(TEST_IFACE_NAME); result.assertResult(TETHER_ERROR_NO_ERROR); } @Test public void testUntether() throws Exception { - when(mTethering.untether(TEST_IFACE_NAME)).thenReturn(TETHER_ERROR_NO_ERROR); - final TestTetheringResult result = new TestTetheringResult(); - mTetheringConnector.untether(TEST_IFACE_NAME, TEST_CALLER_PKG, result); + runAsNoPermission((result) -> { + mTetheringConnector.untether(TEST_IFACE_NAME, TEST_CALLER_PKG, result); + verify(mTethering).isTetherProvisioningRequired(); + result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); + verifyNoMoreInteractionsForTethering(); + }); + + runAsTetherPrivileged((result) -> { + runUnTether(result); + verifyNoMoreInteractionsForTethering(); + }); + + runAsWriteSettings((result) -> { + runUnTether(result); + verify(mTethering).isTetherProvisioningRequired(); + verifyNoMoreInteractionsForTethering(); + }); + } + + private void runSetUsbTethering(final TestTetheringResult result) throws Exception { + when(mTethering.setUsbTethering(true /* enable */)).thenReturn(TETHER_ERROR_NO_ERROR); + mTetheringConnector.setUsbTethering(true /* enable */, TEST_CALLER_PKG, result); verify(mTethering).isTetheringSupported(); - verify(mTethering).untether(TEST_IFACE_NAME); - verifyNoMoreInteractions(mTethering); + verify(mTethering).setUsbTethering(true /* enable */); result.assertResult(TETHER_ERROR_NO_ERROR); } @Test public void testSetUsbTethering() throws Exception { - when(mTethering.setUsbTethering(true /* enable */)).thenReturn(TETHER_ERROR_NO_ERROR); - final TestTetheringResult result = new TestTetheringResult(); - mTetheringConnector.setUsbTethering(true /* enable */, TEST_CALLER_PKG, result); + runAsNoPermission((result) -> { + mTetheringConnector.setUsbTethering(true /* enable */, TEST_CALLER_PKG, result); + verify(mTethering).isTetherProvisioningRequired(); + result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); + verifyNoMoreInteractionsForTethering(); + }); + + runAsTetherPrivileged((result) -> { + runSetUsbTethering(result); + verifyNoMoreInteractionsForTethering(); + }); + + runAsWriteSettings((result) -> { + runSetUsbTethering(result); + verify(mTethering).isTetherProvisioningRequired(); + verifyNoMoreInteractionsForTethering(); + }); + + } + + private void runStartTethering(final TestTetheringResult result, + final TetheringRequestParcel request) throws Exception { + mTetheringConnector.startTethering(request, TEST_CALLER_PKG, result); verify(mTethering).isTetheringSupported(); - verify(mTethering).setUsbTethering(true /* enable */); - verifyNoMoreInteractions(mTethering); - result.assertResult(TETHER_ERROR_NO_ERROR); + verify(mTethering).startTethering(eq(request), eq(result)); } @Test public void testStartTethering() throws Exception { - final TestTetheringResult result = new TestTetheringResult(); final TetheringRequestParcel request = new TetheringRequestParcel(); request.tetheringType = TETHERING_WIFI; - mTetheringConnector.startTethering(request, TEST_CALLER_PKG, result); - verify(mTethering).isTetheringSupported(); - verify(mTethering).startTethering(eq(request), eq(result)); - verifyNoMoreInteractions(mTethering); + + runAsNoPermission((result) -> { + mTetheringConnector.startTethering(request, TEST_CALLER_PKG, result); + verify(mTethering).isTetherProvisioningRequired(); + result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); + verifyNoMoreInteractionsForTethering(); + }); + + runAsTetherPrivileged((result) -> { + runStartTethering(result, request); + verifyNoMoreInteractionsForTethering(); + }); + + runAsWriteSettings((result) -> { + runStartTethering(result, request); + verify(mTethering).isTetherProvisioningRequired(); + verifyNoMoreInteractionsForTethering(); + }); } @Test - public void testStopTethering() throws Exception { - final TestTetheringResult result = new TestTetheringResult(); + public void testStartTetheringWithExemptFromEntitlementCheck() throws Exception { + final TetheringRequestParcel request = new TetheringRequestParcel(); + request.tetheringType = TETHERING_WIFI; + request.exemptFromEntitlementCheck = true; + + runAsTetherPrivileged((result) -> { + runStartTethering(result, request); + verifyNoMoreInteractionsForTethering(); + }); + + runAsWriteSettings((result) -> { + mTetheringConnector.startTethering(request, TEST_CALLER_PKG, result); + result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); + verifyNoMoreInteractionsForTethering(); + }); + } + + private void runStopTethering(final TestTetheringResult result) throws Exception { mTetheringConnector.stopTethering(TETHERING_WIFI, TEST_CALLER_PKG, result); verify(mTethering).isTetheringSupported(); verify(mTethering).stopTethering(TETHERING_WIFI); - verifyNoMoreInteractions(mTethering); result.assertResult(TETHER_ERROR_NO_ERROR); } @Test - public void testRequestLatestTetheringEntitlementResult() throws Exception { - final ResultReceiver result = new ResultReceiver(null); + public void testStopTethering() throws Exception { + runAsNoPermission((result) -> { + mTetheringConnector.stopTethering(TETHERING_WIFI, TEST_CALLER_PKG, result); + verify(mTethering).isTetherProvisioningRequired(); + result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); + verifyNoMoreInteractionsForTethering(); + }); + + runAsTetherPrivileged((result) -> { + runStopTethering(result); + verifyNoMoreInteractionsForTethering(); + }); + + runAsWriteSettings((result) -> { + runStopTethering(result); + verify(mTethering).isTetherProvisioningRequired(); + verifyNoMoreInteractionsForTethering(); + }); + } + + private void runRequestLatestTetheringEntitlementResult() throws Exception { + final MyResultReceiver result = new MyResultReceiver(null); mTetheringConnector.requestLatestTetheringEntitlementResult(TETHERING_WIFI, result, true /* showEntitlementUi */, TEST_CALLER_PKG); verify(mTethering).isTetheringSupported(); verify(mTethering).requestLatestTetheringEntitlementResult(eq(TETHERING_WIFI), eq(result), eq(true) /* showEntitlementUi */); + } + + @Test + public void testRequestLatestTetheringEntitlementResult() throws Exception { + // Run as no permission. + final MyResultReceiver result = new MyResultReceiver(null); + mTetheringConnector.requestLatestTetheringEntitlementResult(TETHERING_WIFI, result, + true /* showEntitlementUi */, TEST_CALLER_PKG); + verify(mTethering).isTetherProvisioningRequired(); + result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); verifyNoMoreInteractions(mTethering); + + runAsTetherPrivileged((none) -> { + runRequestLatestTetheringEntitlementResult(); + verifyNoMoreInteractionsForTethering(); + }); + + runAsWriteSettings((none) -> { + runRequestLatestTetheringEntitlementResult(); + verify(mTethering).isTetherProvisioningRequired(); + verifyNoMoreInteractionsForTethering(); + }); + } + + private void runRegisterTetheringEventCallback() throws Exception { + mTetheringConnector.registerTetheringEventCallback(mITetheringEventCallback, + TEST_CALLER_PKG); + verify(mTethering).registerTetheringEventCallback(eq(mITetheringEventCallback)); } @Test public void testRegisterTetheringEventCallback() throws Exception { - mTetheringConnector.registerTetheringEventCallback(mITetheringEventCallback, + runAsNoPermission((result) -> { + mTetheringConnector.registerTetheringEventCallback(mITetheringEventCallback, + TEST_CALLER_PKG); + verify(mITetheringEventCallback).onCallbackStopped( + TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION); + verifyNoMoreInteractionsForTethering(); + }); + + runAsTetherPrivileged((none) -> { + runRegisterTetheringEventCallback(); + verifyNoMoreInteractionsForTethering(); + }); + + runAsAccessNetworkState((none) -> { + runRegisterTetheringEventCallback(); + verifyNoMoreInteractionsForTethering(); + }); + } + + private void runUnregisterTetheringEventCallback() throws Exception { + mTetheringConnector.unregisterTetheringEventCallback(mITetheringEventCallback, TEST_CALLER_PKG); - verify(mTethering).registerTetheringEventCallback(eq(mITetheringEventCallback)); - verifyNoMoreInteractions(mTethering); + verify(mTethering).unregisterTetheringEventCallback(eq(mITetheringEventCallback)); } @Test public void testUnregisterTetheringEventCallback() throws Exception { - mTetheringConnector.unregisterTetheringEventCallback(mITetheringEventCallback, - TEST_CALLER_PKG); - verify(mTethering).unregisterTetheringEventCallback( - eq(mITetheringEventCallback)); - verifyNoMoreInteractions(mTethering); + runAsNoPermission((result) -> { + mTetheringConnector.unregisterTetheringEventCallback(mITetheringEventCallback, + TEST_CALLER_PKG); + verify(mITetheringEventCallback).onCallbackStopped( + TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION); + verifyNoMoreInteractionsForTethering(); + }); + + runAsTetherPrivileged((none) -> { + runUnregisterTetheringEventCallback(); + verifyNoMoreInteractionsForTethering(); + }); + + runAsAccessNetworkState((none) -> { + runUnregisterTetheringEventCallback(); + verifyNoMoreInteractionsForTethering(); + }); + } + + private void runStopAllTethering(final TestTetheringResult result) throws Exception { + mTetheringConnector.stopAllTethering(TEST_CALLER_PKG, result); + verify(mTethering).isTetheringSupported(); + verify(mTethering).untetherAll(); + result.assertResult(TETHER_ERROR_NO_ERROR); } @Test public void testStopAllTethering() throws Exception { - final TestTetheringResult result = new TestTetheringResult(); - mTetheringConnector.stopAllTethering(TEST_CALLER_PKG, result); + runAsNoPermission((result) -> { + mTetheringConnector.stopAllTethering(TEST_CALLER_PKG, result); + verify(mTethering).isTetherProvisioningRequired(); + result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); + verifyNoMoreInteractionsForTethering(); + }); + + runAsTetherPrivileged((result) -> { + runStopAllTethering(result); + verifyNoMoreInteractionsForTethering(); + }); + + runAsWriteSettings((result) -> { + runStopAllTethering(result); + verify(mTethering).isTetherProvisioningRequired(); + verifyNoMoreInteractionsForTethering(); + }); + } + + private void runIsTetheringSupported(final TestTetheringResult result) throws Exception { + mTetheringConnector.isTetheringSupported(TEST_CALLER_PKG, result); verify(mTethering).isTetheringSupported(); - verify(mTethering).untetherAll(); - verifyNoMoreInteractions(mTethering); result.assertResult(TETHER_ERROR_NO_ERROR); } @Test public void testIsTetheringSupported() throws Exception { - final TestTetheringResult result = new TestTetheringResult(); - mTetheringConnector.isTetheringSupported(TEST_CALLER_PKG, result); - verify(mTethering).isTetheringSupported(); - verifyNoMoreInteractions(mTethering); - result.assertResult(TETHER_ERROR_NO_ERROR); + runAsNoPermission((result) -> { + mTetheringConnector.isTetheringSupported(TEST_CALLER_PKG, result); + verify(mTethering).isTetherProvisioningRequired(); + result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); + verifyNoMoreInteractionsForTethering(); + }); + + runAsTetherPrivileged((result) -> { + runIsTetheringSupported(result); + verifyNoMoreInteractionsForTethering(); + }); + + runAsWriteSettings((result) -> { + runIsTetheringSupported(result); + verify(mTethering).isTetherProvisioningRequired(); + verifyNoMoreInteractionsForTethering(); + }); } } From cd309138ceda1bdb1cd74aeede1d2585d2b637b3 Mon Sep 17 00:00:00 2001 From: Mark Chien Date: Mon, 25 May 2020 02:04:59 +0000 Subject: [PATCH 144/188] Adjust TTL for ipv6 tethering If upstream is cellular, set the TTL in Router Advertisements to "network-set TTL - 1" for carrier requirement. For other non-cellular upstream, set TTL as "network-set TTL + 1" to preventing arbitrary distinction between tethered and untethered traffic. Bug: 154776299 Test: atest TetheringTests Merged-In: I7f2696a642f96c6aafb5613b980bf5bcdd08bbda Change-Id: I7f2696a642f96c6aafb5613b980bf5bcdd08bbda --- Tethering/src/android/net/ip/IpServer.java | 13 ++-- .../tethering/IPv6TetheringCoordinator.java | 19 +++++- .../unit/src/android/net/ip/IpServerTest.java | 62 ++++++++++++++++--- .../IPv6TetheringCoordinatorTest.java | 4 +- 4 files changed, 80 insertions(+), 18 deletions(-) diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java index de537871a7..659d344acf 100644 --- a/Tethering/src/android/net/ip/IpServer.java +++ b/Tethering/src/android/net/ip/IpServer.java @@ -738,7 +738,7 @@ public class IpServer extends StateMachine { // // TODO: Evaluate using a data structure than is more directly suited to // communicating only the relevant information. - private void updateUpstreamIPv6LinkProperties(LinkProperties v6only) { + private void updateUpstreamIPv6LinkProperties(LinkProperties v6only, int ttlAdjustment) { if (mRaDaemon == null) return; // Avoid unnecessary work on spurious updates. @@ -761,7 +761,7 @@ public class IpServer extends StateMachine { params.mtu = mUsingBpfOffload ? v6only.getMtu() - 16 : v6only.getMtu(); params.hasDefaultRoute = v6only.hasIpv6DefaultRoute(); - if (params.hasDefaultRoute) params.hopLimit = getHopLimit(upstreamIface); + if (params.hasDefaultRoute) params.hopLimit = getHopLimit(upstreamIface, ttlAdjustment); for (LinkAddress linkAddr : v6only.getLinkAddresses()) { if (linkAddr.getPrefixLength() != RFC7421_PREFIX_LENGTH) continue; @@ -1052,12 +1052,11 @@ public class IpServer extends StateMachine { } } - private byte getHopLimit(String upstreamIface) { + private byte getHopLimit(String upstreamIface, int adjustTTL) { try { int upstreamHopLimit = Integer.parseUnsignedInt( mNetd.getProcSysNet(INetd.IPV6, INetd.CONF, upstreamIface, "hop_limit")); - // Add one hop to account for this forwarding device - upstreamHopLimit++; + upstreamHopLimit = upstreamHopLimit + adjustTTL; // Cap the hop limit to 255. return (byte) Integer.min(upstreamHopLimit, 255); } catch (Exception e) { @@ -1145,7 +1144,7 @@ public class IpServer extends StateMachine { transitionTo(mUnavailableState); break; case CMD_IPV6_TETHER_UPDATE: - updateUpstreamIPv6LinkProperties((LinkProperties) message.obj); + updateUpstreamIPv6LinkProperties((LinkProperties) message.obj, message.arg1); break; default: return NOT_HANDLED; @@ -1209,7 +1208,7 @@ public class IpServer extends StateMachine { if (DBG) Log.d(TAG, "Untethered (ifdown)" + mIfaceName); break; case CMD_IPV6_TETHER_UPDATE: - updateUpstreamIPv6LinkProperties((LinkProperties) message.obj); + updateUpstreamIPv6LinkProperties((LinkProperties) message.obj, message.arg1); sendLinkProperties(); break; case CMD_IP_FORWARDING_ENABLE_ERROR: diff --git a/Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java b/Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java index d450c46de7..f3dcaa2529 100644 --- a/Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java +++ b/Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java @@ -161,11 +161,28 @@ public class IPv6TetheringCoordinator { private void updateIPv6TetheringInterfaces() { for (IpServer ipServer : mNotifyList) { final LinkProperties lp = getInterfaceIPv6LinkProperties(ipServer); - ipServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, lp); + ipServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, getTtlAdjustment(), 0, lp); break; } } + private int getTtlAdjustment() { + if (mUpstreamNetworkState == null || mUpstreamNetworkState.networkCapabilities == null) { + return 0; + } + + // If upstream is cellular, set the TTL in Router Advertisements to "network-set TTL" - 1 + // for carrier requirement. + if (mUpstreamNetworkState.networkCapabilities.hasTransport( + NetworkCapabilities.TRANSPORT_CELLULAR)) { + return -1; + } + + // For other non-cellular upstream, set TTL as "network-set TTL" + 1 to preventing arbitrary + // distinction between tethered and untethered traffic. + return 1; + } + private LinkProperties getInterfaceIPv6LinkProperties(IpServer ipServer) { final Downstream ds = findDownstream(ipServer); if (ds == null) return null; diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java index cd1ff607b4..307ebf17d2 100644 --- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java +++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java @@ -17,6 +17,7 @@ package android.net.ip; import static android.net.INetd.IF_STATE_UP; +import static android.net.RouteInfo.RTN_UNICAST; import static android.net.TetheringManager.TETHERING_BLUETOOTH; import static android.net.TetheringManager.TETHERING_NCM; import static android.net.TetheringManager.TETHERING_USB; @@ -74,6 +75,7 @@ import android.net.dhcp.IDhcpServer; import android.net.dhcp.IDhcpServerCallbacks; import android.net.ip.IpNeighborMonitor.NeighborEvent; import android.net.ip.IpNeighborMonitor.NeighborEventConsumer; +import android.net.ip.RouterAdvertisementDaemon.RaParams; import android.net.util.InterfaceParams; import android.net.util.InterfaceSet; import android.net.util.SharedLog; @@ -196,7 +198,7 @@ public class IpServerTest { if (upstreamIface != null) { LinkProperties lp = new LinkProperties(); lp.setInterfaceName(upstreamIface); - dispatchTetherConnectionChanged(upstreamIface, lp); + dispatchTetherConnectionChanged(upstreamIface, lp, 0); } reset(mNetd, mCallback); } @@ -694,7 +696,7 @@ public class IpServerTest { InOrder inOrder = inOrder(mNetd); LinkProperties lp = new LinkProperties(); lp.setInterfaceName(UPSTREAM_IFACE2); - dispatchTetherConnectionChanged(UPSTREAM_IFACE2, lp); + dispatchTetherConnectionChanged(UPSTREAM_IFACE2, lp, -1); inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX2, neighA, macA)); inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macA)); inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX2, neighB, macB)); @@ -702,7 +704,7 @@ public class IpServerTest { reset(mNetd); // When the upstream is lost, rules are removed. - dispatchTetherConnectionChanged(null, null); + dispatchTetherConnectionChanged(null, null, 0); verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX2, neighA, macA)); verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX2, neighB, macB)); reset(mNetd); @@ -715,19 +717,19 @@ public class IpServerTest { // Rules can be added again once upstream IPv6 connectivity is available. lp.setInterfaceName(UPSTREAM_IFACE); - dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp); + dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, -1); recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB); verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighB, macB)); verify(mNetd, never()).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA)); // If upstream IPv6 connectivity is lost, rules are removed. reset(mNetd); - dispatchTetherConnectionChanged(UPSTREAM_IFACE, null); + dispatchTetherConnectionChanged(UPSTREAM_IFACE, null, 0); verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB)); // When the interface goes down, rules are removed. lp.setInterfaceName(UPSTREAM_IFACE); - dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp); + dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, -1); recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA); recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB); verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA)); @@ -788,6 +790,49 @@ public class IpServerTest { verify(mIpNeighborMonitor, never()).start(); } + private LinkProperties buildIpv6OnlyLinkProperties(final String iface) { + final LinkProperties linkProp = new LinkProperties(); + linkProp.setInterfaceName(iface); + linkProp.addLinkAddress(new LinkAddress("2001:db8::1/64")); + linkProp.addRoute(new RouteInfo(new IpPrefix("::/0"), null, iface, RTN_UNICAST)); + final InetAddress dns = InetAddresses.parseNumericAddress("2001:4860:4860::8888"); + linkProp.addDnsServer(dns); + + return linkProp; + } + + @Test + public void testAdjustTtlValue() throws Exception { + final ArgumentCaptor raParamsCaptor = + ArgumentCaptor.forClass(RaParams.class); + initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE); + verify(mRaDaemon).buildNewRa(any(), raParamsCaptor.capture()); + final RaParams noV6Params = raParamsCaptor.getValue(); + assertEquals(65, noV6Params.hopLimit); + reset(mRaDaemon); + + when(mNetd.getProcSysNet( + INetd.IPV6, INetd.CONF, UPSTREAM_IFACE, "hop_limit")).thenReturn("64"); + final LinkProperties lp = buildIpv6OnlyLinkProperties(UPSTREAM_IFACE); + dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, 1); + verify(mRaDaemon).buildNewRa(any(), raParamsCaptor.capture()); + final RaParams nonCellularParams = raParamsCaptor.getValue(); + assertEquals(65, nonCellularParams.hopLimit); + reset(mRaDaemon); + + dispatchTetherConnectionChanged(UPSTREAM_IFACE, null, 0); + verify(mRaDaemon).buildNewRa(any(), raParamsCaptor.capture()); + final RaParams noUpstream = raParamsCaptor.getValue(); + assertEquals(65, nonCellularParams.hopLimit); + reset(mRaDaemon); + + dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, -1); + verify(mRaDaemon).buildNewRa(any(), raParamsCaptor.capture()); + final RaParams cellularParams = raParamsCaptor.getValue(); + assertEquals(63, cellularParams.hopLimit); + reset(mRaDaemon); + } + private void assertDhcpServingParams(final DhcpServingParamsParcel params, final IpPrefix prefix) { // Last address byte is random @@ -838,9 +883,10 @@ public class IpServerTest { * @param upstreamIface String name of upstream interface (or null) * @param v6lp IPv6 LinkProperties of the upstream interface, or null for an IPv4-only upstream. */ - private void dispatchTetherConnectionChanged(String upstreamIface, LinkProperties v6lp) { + private void dispatchTetherConnectionChanged(String upstreamIface, LinkProperties v6lp, + int ttlAdjustment) { dispatchTetherConnectionChanged(upstreamIface); - mIpServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, v6lp); + mIpServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, ttlAdjustment, 0, v6lp); mLooper.dispatchAll(); } diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/IPv6TetheringCoordinatorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/IPv6TetheringCoordinatorTest.java index 820f255145..f2b5314e5a 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/IPv6TetheringCoordinatorTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/IPv6TetheringCoordinatorTest.java @@ -128,7 +128,7 @@ public class IPv6TetheringCoordinatorTest { final UpstreamNetworkState mobileUpstream = createDualStackUpstream(TRANSPORT_CELLULAR); final ArgumentCaptor lp = ArgumentCaptor.forClass(LinkProperties.class); mIPv6TetheringCoordinator.updateUpstreamNetworkState(mobileUpstream); - verify(firstServer).sendMessage(eq(IpServer.CMD_IPV6_TETHER_UPDATE), eq(0), eq(0), + verify(firstServer).sendMessage(eq(IpServer.CMD_IPV6_TETHER_UPDATE), eq(-1), eq(0), lp.capture()); final LinkProperties v6OnlyLink = lp.getValue(); assertOnlyOneV6AddressAndNoV4(v6OnlyLink); @@ -140,7 +140,7 @@ public class IPv6TetheringCoordinatorTest { mNotifyList.remove(firstServer); mIPv6TetheringCoordinator.removeActiveDownstream(firstServer); verify(firstServer).sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null); - verify(secondServer).sendMessage(eq(IpServer.CMD_IPV6_TETHER_UPDATE), eq(0), eq(0), + verify(secondServer).sendMessage(eq(IpServer.CMD_IPV6_TETHER_UPDATE), eq(-1), eq(0), lp.capture()); final LinkProperties localOnlyLink = lp.getValue(); assertNotNull(localOnlyLink); From 369fd25f791eb3a654c082120803ac390ae1f39d Mon Sep 17 00:00:00 2001 From: Treehugger Robot Date: Wed, 27 May 2020 04:17:44 +0000 Subject: [PATCH 145/188] Remove platform cert from Tethering tests Bug: 156866746 Test: atest TetheringTests, TetheringCoverageTests Merged-In: I7c539f1f4a447b5913164b222601c6113c6fe645 Change-Id: I7c539f1f4a447b5913164b222601c6113c6fe645 --- Tethering/tests/integration/Android.bp | 1 - Tethering/tests/unit/Android.bp | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Tethering/tests/integration/Android.bp b/Tethering/tests/integration/Android.bp index 3305ed0844..ed69b7d63c 100644 --- a/Tethering/tests/integration/Android.bp +++ b/Tethering/tests/integration/Android.bp @@ -63,7 +63,6 @@ android_test { // NetworkStackTests. android_test { name: "TetheringCoverageTests", - certificate: "platform", platform_apis: true, test_suites: ["device-tests", "mts"], test_config: "AndroidTest_Coverage.xml", diff --git a/Tethering/tests/unit/Android.bp b/Tethering/tests/unit/Android.bp index 08cfb30813..e00435b0c3 100644 --- a/Tethering/tests/unit/Android.bp +++ b/Tethering/tests/unit/Android.bp @@ -83,7 +83,7 @@ android_library { android_test { name: "TetheringTests", - certificate: "platform", + platform_apis: true, test_suites: [ "device-tests", "mts", From 9c9c600c16bc4fa6f5794e98e91d07154e592541 Mon Sep 17 00:00:00 2001 From: Treehugger Robot Date: Thu, 28 May 2020 08:50:08 +0000 Subject: [PATCH 146/188] Extend timeout for requesting tethered interface Extend the timeout to lower the EthernetTetheringTest flaky. Besides, also explicitly exempt entitlement check in EtetherntTetheringTest because it do not test tethering upstream currently. Thus, the tests would not be interrupted by entitlement check if test SIM is entitlement required. Bug: 156713866 Test: TetheringCoverageTests, CtsTehteringTest Merged-In: I45e8e8d737486def9d0de8943ec7f09ca0942a0b Change-Id: I45e8e8d737486def9d0de8943ec7f09ca0942a0b --- .../src/android/net/EthernetTetheringTest.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java index 4bac9da9c3..2fb7e607d0 100644 --- a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java +++ b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java @@ -76,7 +76,7 @@ import java.util.concurrent.TimeoutException; public class EthernetTetheringTest { private static final String TAG = EthernetTetheringTest.class.getSimpleName(); - private static final int TIMEOUT_MS = 1000; + private static final int TIMEOUT_MS = 5000; private static final int PACKET_READ_TIMEOUT_MS = 100; private static final int DHCP_DISCOVER_ATTEMPTS = 10; private static final byte[] DHCP_REQUESTED_PARAMS = new byte[] { @@ -338,7 +338,8 @@ public class EthernetTetheringTest { private MyTetheringEventCallback enableEthernetTethering(String iface) throws Exception { return enableEthernetTethering(iface, - new TetheringRequest.Builder(TETHERING_ETHERNET).build()); + new TetheringRequest.Builder(TETHERING_ETHERNET) + .setExemptFromEntitlementCheck(true).build()); } private int getMTU(TestNetworkInterface iface) throws SocketException { @@ -508,7 +509,8 @@ public class EthernetTetheringTest { LinkAddress localAddr = local == null ? null : new LinkAddress(local); LinkAddress clientAddr = client == null ? null : new LinkAddress(client); return new TetheringRequest.Builder(TETHERING_ETHERNET) - .setStaticIpv4Addresses(localAddr, clientAddr).build(); + .setStaticIpv4Addresses(localAddr, clientAddr) + .setExemptFromEntitlementCheck(true).build(); } private void assertInvalidStaticIpv4Request(String iface, String local, String client) From 6c633de59e7652e4f84c28728d468c6a70cb529b Mon Sep 17 00:00:00 2001 From: paulhu Date: Thu, 28 May 2020 19:17:45 +0800 Subject: [PATCH 147/188] Use Class#getSimpleName instead of KClass#getSimpleName KClass#getSimpleName need refer to kotlin-reflect.jar which need include it in Andorid.bp. However, it's not necessary to use KClass#getSimpleName but use Class#getSimpleName instead. Test: atest TetheringTests Bug: 157527499 Change-Id: I49bc336a276d30152402eba926cc583bc81e8e5c --- .../networkstack/tethering/TetheringNotificationUpdaterTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt index 745468fdf3..7d5471f770 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt @@ -130,7 +130,7 @@ class TetheringNotificationUpdaterTest { context = TestContext(InstrumentationRegistry.getInstrumentation().context) doReturn(notificationManager).`when`(mockContext) .getSystemService(Context.NOTIFICATION_SERVICE) - fakeTetheringThread = HandlerThread(this::class.simpleName) + fakeTetheringThread = HandlerThread(this::class.java.simpleName) fakeTetheringThread.start() notificationUpdater = WrappedNotificationUpdater(context, fakeTetheringThread.looper) setupResources() From cbac8e0b63438d76653cfdfd0ba7be28c29c3f45 Mon Sep 17 00:00:00 2001 From: Paul Duffin Date: Wed, 13 May 2020 12:28:49 +0100 Subject: [PATCH 148/188] Switch framework-tethering to use java_sdk_library The names of the individual modules do not quite follow the pattern that java_sdk_library uses so this temporarily sets the following: naming_scheme: "frameworks-modules" That causes java_sdk_library to use a naming scheme that matches the one used by the individual modules of this. It will be cleaned up later. Part of the purpose of the java_sdk_library is to hide the implementation code and force users of the library to depend on stubs for a well defined API. Ideally, it would allow access to the implementation in those cases where it is safe, e.g. from within the same APEX, or from tests for the implementation. Unfortunately, due to limitations in the build it does not yet have enough information to make that decision correctly which means that any code that needs to compile against the implementation is broken which would prevent us from converting the module to java_sdk_library. However, the only way to provide the additional information to allow the implementation to be correctly exposed is to convert the modules to java_sdk_library; a cycle. In order to break that cycle the java_sdk_library creates a special .impl target which is used directly by tests and any other code that needs it. Once all the modules have been converted to a java_sdk_library then we can resolve the limitations in the build and remove the direct references to .impl. Test: m Tethering InProcessTethering checkapi Bug: 155164730 Merged-In: If5c115f482751f9f4b5f047e9e401a18e36799ef Change-Id: Id1c2e848430c49a2da7402244814cd084f5da77c --- Tethering/Android.bp | 2 +- Tethering/common/TetheringLib/Android.bp | 101 ++--------------------- Tethering/tests/unit/Android.bp | 4 +- 3 files changed, 12 insertions(+), 95 deletions(-) diff --git a/Tethering/Android.bp b/Tethering/Android.bp index 1ee017be50..8ae30a5c6f 100644 --- a/Tethering/Android.bp +++ b/Tethering/Android.bp @@ -33,7 +33,7 @@ java_defaults { "net-utils-framework-common", ], libs: [ - "framework-tethering", + "framework-tethering.impl", "framework-wifi-stubs-systemapi", "unsupportedappusage", ], diff --git a/Tethering/common/TetheringLib/Android.bp b/Tethering/common/TetheringLib/Android.bp index ae4bb3e5e2..408725c865 100644 --- a/Tethering/common/TetheringLib/Android.bp +++ b/Tethering/common/TetheringLib/Android.bp @@ -13,31 +13,28 @@ // See the License for the specific language governing permissions and // limitations under the License. -java_library { +java_sdk_library { name: "framework-tethering", - sdk_version: "module_current", + defaults: ["framework-module-defaults"], srcs: [ ":framework-tethering-srcs", ], + + // TODO(b/155480189) - Remove naming_scheme once references have been resolved. + // Temporary java_sdk_library component naming scheme to use to ease the transition from separate + // modules to java_sdk_library. + naming_scheme: "framework-modules", + jarjar_rules: "jarjar-rules.txt", installable: true, - libs: [ - "framework-annotations-lib", - ], - hostdex: true, // for hiddenapi check visibility: ["//frameworks/base/packages/Tethering:__subpackages__"], + stubs_library_visibility: ["//visibility:public"], apex_available: ["com.android.tethering"], permitted_packages: ["android.net"], } -stubs_defaults { - name: "framework-tethering-stubs-defaults", - srcs: [":framework-tethering-srcs"], - dist: { dest: "framework-tethering.txt" }, -} - filegroup { name: "framework-tethering-srcs", srcs: [ @@ -55,83 +52,3 @@ filegroup { ], path: "src" } - -droidstubs { - name: "framework-tethering-stubs-srcs-publicapi", - defaults: [ - "framework-module-stubs-defaults-publicapi", - "framework-tethering-stubs-defaults", - ], - check_api: { - last_released: { - api_file: ":framework-tethering.api.public.latest", - removed_api_file: ":framework-tethering-removed.api.public.latest", - }, - api_lint: { - new_since: ":framework-tethering.api.public.latest", - }, - }, -} - -droidstubs { - name: "framework-tethering-stubs-srcs-systemapi", - defaults: [ - "framework-module-stubs-defaults-systemapi", - "framework-tethering-stubs-defaults", - ], - check_api: { - last_released: { - api_file: ":framework-tethering.api.system.latest", - removed_api_file: ":framework-tethering-removed.api.system.latest", - }, - api_lint: { - new_since: ":framework-tethering.api.system.latest", - }, - }, -} - -droidstubs { - name: "framework-tethering-api-module_libs_api", - defaults: [ - "framework-module-api-defaults-module_libs_api", - "framework-tethering-stubs-defaults", - ], - check_api: { - last_released: { - api_file: ":framework-tethering.api.module-lib.latest", - removed_api_file: ":framework-tethering-removed.api.module-lib.latest", - }, - api_lint: { - new_since: ":framework-tethering.api.module-lib.latest", - }, - }, -} - -droidstubs { - name: "framework-tethering-stubs-srcs-module_libs_api", - defaults: [ - "framework-module-stubs-defaults-module_libs_api", - "framework-tethering-stubs-defaults", - ], -} - -java_library { - name: "framework-tethering-stubs-publicapi", - srcs: [":framework-tethering-stubs-srcs-publicapi"], - defaults: ["framework-module-stubs-lib-defaults-publicapi"], - dist: { dest: "framework-tethering.jar" }, -} - -java_library { - name: "framework-tethering-stubs-systemapi", - srcs: [":framework-tethering-stubs-srcs-systemapi"], - defaults: ["framework-module-stubs-lib-defaults-systemapi"], - dist: { dest: "framework-tethering.jar" }, -} - -java_library { - name: "framework-tethering-stubs-module_libs_api", - srcs: [":framework-tethering-stubs-srcs-module_libs_api"], - defaults: ["framework-module-stubs-lib-defaults-module_libs_api"], - dist: { dest: "framework-tethering.jar" }, -} diff --git a/Tethering/tests/unit/Android.bp b/Tethering/tests/unit/Android.bp index e00435b0c3..fccc6902e3 100644 --- a/Tethering/tests/unit/Android.bp +++ b/Tethering/tests/unit/Android.bp @@ -29,7 +29,7 @@ java_library { sdk_version: "core_platform", libs: [ "framework-minus-apex", - "framework-tethering", + "framework-tethering.impl", ], visibility: ["//cts/tests/tests/tethering"], } @@ -59,7 +59,7 @@ java_defaults { "ext", "framework-minus-apex", "framework-res", - "framework-tethering", + "framework-tethering.impl", "framework-wifi-stubs-module_libs_api", ], jni_libs: [ From b5898ad53a147cf94565f55d2f6aef5bba18af61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Thu, 28 May 2020 03:21:31 -0700 Subject: [PATCH 149/188] Stop reducing RA advertised ipv6 mtu by 16 - not needed. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This effectively reverts: commit da0fb1bca8eda1ce8159289a2ea9e4f6933ce517 Author: Maciej Żenczykowski Date: Wed Feb 19 01:24:39 2020 -0800 Reduce advertised ipv6 mtu by 16 to fit ethernet header This is a temporary hack to workaround the inability of current kernel's ebpf bpf_skb_change_mode() function to prefix a 14-byte ethernet header on to a packet without going over the upstream (source, rawip) interface's mtu *before* we bpf_redirect() to the downstream (destination, ethernet) interface. Test: build, atest, atest TetheringTests Bug: 149816401 Test: flashed a flame with new kernel and it works at 1500 mtu Bug: 149816401 Signed-off-by: Maciej Żenczykowski Change-Id: I76a75a16fa27b47d78816b2f9379ef4bb68beb00 --- Tethering/src/android/net/ip/IpServer.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java index 659d344acf..6edc0e6f87 100644 --- a/Tethering/src/android/net/ip/IpServer.java +++ b/Tethering/src/android/net/ip/IpServer.java @@ -753,12 +753,7 @@ public class IpServer extends StateMachine { final String upstreamIface = v6only.getInterfaceName(); params = new RaParams(); - // When BPF offload is enabled, we advertise an mtu lower by 16, which is the closest - // multiple of 8 >= 14, the ethernet header size. This makes kernel ebpf tethering - // offload happy. This hack should be reverted once we have the kernel fixed up. - // Note: this will automatically clamp to at least 1280 (ipv6 minimum mtu) - // see RouterAdvertisementDaemon.java putMtu() - params.mtu = mUsingBpfOffload ? v6only.getMtu() - 16 : v6only.getMtu(); + params.mtu = v6only.getMtu(); params.hasDefaultRoute = v6only.hasIpv6DefaultRoute(); if (params.hasDefaultRoute) params.hopLimit = getHopLimit(upstreamIface, ttlAdjustment); From 2ecd333f5971519f87c3043b5b152e510dfe7db0 Mon Sep 17 00:00:00 2001 From: Mark Chien Date: Fri, 29 May 2020 22:07:37 +0000 Subject: [PATCH 150/188] Tethering: ensure downstream prefix do not conflict with upstream - Add New class PrivateAddressCoordinator to coordinate the private address conflict problem. - Downstream prefix would be random in 192.168.0.0/24 ~ 192.168.255.0/24. - If new upstream prefix is conflict with existing downstream prefix, downstream would be kicked out and it would request a new one. - The last conflict upstream prefixes would be blacklist. Avoid to select downstream prefix which is conflict with prefixes in blacklist. Bug: 130879722 Test: -build, flash, boot -atest TetheringTests Merged-In: Ib45b87bcd9eeb5da03fb7ec90b1af9ca53998cf5 Change-Id: Ib45b87bcd9eeb5da03fb7ec90b1af9ca53998cf5 --- Tethering/src/android/net/ip/IpServer.java | 171 ++++++------ .../tethering/PrivateAddressCoordinator.java | 254 ++++++++++++++++++ .../networkstack/tethering/Tethering.java | 32 ++- .../unit/src/android/net/ip/IpServerTest.java | 67 +++-- .../PrivateAddressCoordinatorTest.java | 254 ++++++++++++++++++ .../networkstack/tethering/TetheringTest.java | 116 +++++++- 6 files changed, 775 insertions(+), 119 deletions(-) create mode 100644 Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java create mode 100644 Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java index 659d344acf..f08429bb06 100644 --- a/Tethering/src/android/net/ip/IpServer.java +++ b/Tethering/src/android/net/ip/IpServer.java @@ -16,14 +16,13 @@ package android.net.ip; -import static android.net.InetAddresses.parseNumericAddress; import static android.net.RouteInfo.RTN_UNICAST; import static android.net.TetheringManager.TetheringRequest.checkStaticAddressConfiguration; import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS; import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH; -import static android.net.util.NetworkConstants.FF; import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH; import static android.net.util.NetworkConstants.asByte; +import static android.net.util.PrefixUtils.asIpPrefix; import static android.net.util.TetheringMessageBase.BASE_IPSERVER; import static android.system.OsConstants.RT_SCOPE_UNIVERSE; @@ -66,11 +65,11 @@ import androidx.annotation.Nullable; import com.android.internal.util.MessageUtils; import com.android.internal.util.State; import com.android.internal.util.StateMachine; +import com.android.networkstack.tethering.PrivateAddressCoordinator; import java.io.IOException; import java.net.Inet4Address; import java.net.Inet6Address; -import java.net.InetAddress; import java.net.NetworkInterface; import java.net.UnknownHostException; import java.util.ArrayList; @@ -108,27 +107,8 @@ public class IpServer extends StateMachine { private static final byte DOUG_ADAMS = (byte) 42; - private static final String USB_NEAR_IFACE_ADDR = "192.168.42.129"; - private static final int USB_PREFIX_LENGTH = 24; - private static final String WIFI_HOST_IFACE_ADDR = "192.168.43.1"; - private static final int WIFI_HOST_IFACE_PREFIX_LENGTH = 24; - private static final String WIFI_P2P_IFACE_ADDR = "192.168.49.1"; - private static final int WIFI_P2P_IFACE_PREFIX_LENGTH = 24; - private static final String ETHERNET_IFACE_ADDR = "192.168.50.1"; - private static final int ETHERNET_IFACE_PREFIX_LENGTH = 24; - - // TODO: remove this constant after introducing PrivateAddressCoordinator. - private static final List NCM_PREFIXES = Collections.unmodifiableList( - Arrays.asList( - new IpPrefix("192.168.42.0/24"), - new IpPrefix("192.168.51.0/24"), - new IpPrefix("192.168.52.0/24"), - new IpPrefix("192.168.53.0/24") - )); - // TODO: have PanService use some visible version of this constant - private static final String BLUETOOTH_IFACE_ADDR = "192.168.44.1"; - private static final int BLUETOOTH_DHCP_PREFIX_LENGTH = 24; + private static final String BLUETOOTH_IFACE_ADDR = "192.168.44.1/24"; // TODO: have this configurable private static final int DHCP_LEASE_TIME_SECS = 3600; @@ -167,6 +147,14 @@ public class IpServer extends StateMachine { * Notify that the DHCP leases changed in one of the IpServers. */ public void dhcpLeasesChanged() { } + + /** + * Request Tethering change. + * + * @param tetheringType the downstream type of this IpServer. + * @param enabled enable or disable tethering. + */ + public void requestEnableTethering(int tetheringType, boolean enabled) { } } /** Capture IpServer dependencies, for injection. */ @@ -196,6 +184,7 @@ public class IpServer extends StateMachine { return 0; } } + /** Create a DhcpServer instance to be used by IpServer. */ public abstract void makeDhcpServer(String ifName, DhcpServingParamsParcel params, DhcpServerCallbacks cb); @@ -225,16 +214,20 @@ public class IpServer extends StateMachine { public static final int CMD_NEIGHBOR_EVENT = BASE_IPSERVER + 11; // request from DHCP server that it wants to have a new prefix public static final int CMD_NEW_PREFIX_REQUEST = BASE_IPSERVER + 12; + // request from PrivateAddressCoordinator to restart tethering. + public static final int CMD_NOTIFY_PREFIX_CONFLICT = BASE_IPSERVER + 13; private final State mInitialState; private final State mLocalHotspotState; private final State mTetheredState; private final State mUnavailableState; + private final State mWaitingForRestartState; private final SharedLog mLog; private final INetd mNetd; private final Callback mCallback; private final InterfaceController mInterfaceCtrl; + private final PrivateAddressCoordinator mPrivateAddressCoordinator; private final String mIfaceName; private final int mInterfaceType; @@ -261,7 +254,6 @@ public class IpServer extends StateMachine { private int mDhcpServerStartIndex = 0; private IDhcpServer mDhcpServer; private RaParams mLastRaParams; - private LinkAddress mIpv4Address; private LinkAddress mStaticIpv4ServerAddr; private LinkAddress mStaticIpv4ClientAddr; @@ -316,12 +308,14 @@ public class IpServer extends StateMachine { private final IpNeighborMonitor mIpNeighborMonitor; + private LinkAddress mIpv4Address; + // TODO: Add a dependency object to pass the data members or variables from the tethering // object. It helps to reduce the arguments of the constructor. public IpServer( String ifaceName, Looper looper, int interfaceType, SharedLog log, INetd netd, Callback callback, boolean usingLegacyDhcp, boolean usingBpfOffload, - Dependencies deps) { + PrivateAddressCoordinator addressCoordinator, Dependencies deps) { super(ifaceName, looper); mLog = log.forSubComponent(ifaceName); mNetd = netd; @@ -332,6 +326,7 @@ public class IpServer extends StateMachine { mLinkProperties = new LinkProperties(); mUsingLegacyDhcp = usingLegacyDhcp; mUsingBpfOffload = usingBpfOffload; + mPrivateAddressCoordinator = addressCoordinator; mDeps = deps; resetLinkProperties(); mLastError = TetheringManager.TETHER_ERROR_NO_ERROR; @@ -352,9 +347,11 @@ public class IpServer extends StateMachine { mLocalHotspotState = new LocalHotspotState(); mTetheredState = new TetheredState(); mUnavailableState = new UnavailableState(); + mWaitingForRestartState = new WaitingForRestartState(); addState(mInitialState); addState(mLocalHotspotState); addState(mTetheredState); + addState(mWaitingForRestartState, mTetheredState); addState(mUnavailableState); setInitialState(mInitialState); @@ -387,6 +384,11 @@ public class IpServer extends StateMachine { return new LinkProperties(mLinkProperties); } + /** The address which IpServer is using. */ + public LinkAddress getAddress() { + return mIpv4Address; + } + /** * Get the latest list of DHCP leases that was reported. Must be called on the IpServer looper * thread. @@ -617,6 +619,7 @@ public class IpServer extends StateMachine { // NOTE: All of configureIPv4() will be refactored out of existence // into calls to InterfaceController, shared with startIPv4(). mInterfaceCtrl.clearIPv4Address(); + mPrivateAddressCoordinator.releaseDownstream(this); mIpv4Address = null; mStaticIpv4ServerAddr = null; mStaticIpv4ClientAddr = null; @@ -625,43 +628,24 @@ public class IpServer extends StateMachine { private boolean configureIPv4(boolean enabled) { if (VDBG) Log.d(TAG, "configureIPv4(" + enabled + ")"); - // TODO: Replace this hard-coded information with dynamically selected - // config passed down to us by a higher layer IP-coordinating element. - final Inet4Address srvAddr; - int prefixLen = 0; - try { - if (mStaticIpv4ServerAddr != null) { - srvAddr = (Inet4Address) mStaticIpv4ServerAddr.getAddress(); - prefixLen = mStaticIpv4ServerAddr.getPrefixLength(); - } else if (mInterfaceType == TetheringManager.TETHERING_USB - || mInterfaceType == TetheringManager.TETHERING_NCM) { - srvAddr = (Inet4Address) parseNumericAddress(USB_NEAR_IFACE_ADDR); - prefixLen = USB_PREFIX_LENGTH; - } else if (mInterfaceType == TetheringManager.TETHERING_WIFI) { - srvAddr = (Inet4Address) parseNumericAddress(getRandomWifiIPv4Address()); - prefixLen = WIFI_HOST_IFACE_PREFIX_LENGTH; - } else if (mInterfaceType == TetheringManager.TETHERING_WIFI_P2P) { - srvAddr = (Inet4Address) parseNumericAddress(WIFI_P2P_IFACE_ADDR); - prefixLen = WIFI_P2P_IFACE_PREFIX_LENGTH; - } else if (mInterfaceType == TetheringManager.TETHERING_ETHERNET) { - // TODO: randomize address for tethering too, similarly to wifi - srvAddr = (Inet4Address) parseNumericAddress(ETHERNET_IFACE_ADDR); - prefixLen = ETHERNET_IFACE_PREFIX_LENGTH; - } else { - // BT configures the interface elsewhere: only start DHCP. - // TODO: make all tethering types behave the same way, and delete the bluetooth - // code that calls into NetworkManagementService directly. - srvAddr = (Inet4Address) parseNumericAddress(BLUETOOTH_IFACE_ADDR); - mIpv4Address = new LinkAddress(srvAddr, BLUETOOTH_DHCP_PREFIX_LENGTH); - return configureDhcp(enabled, mIpv4Address, null /* clientAddress */); - } - mIpv4Address = new LinkAddress(srvAddr, prefixLen); - } catch (IllegalArgumentException e) { - mLog.e("Error selecting ipv4 address", e); - if (!enabled) stopDhcp(); + if (enabled) { + mIpv4Address = requestIpv4Address(); + } + + if (mIpv4Address == null) { + mLog.e("No available ipv4 address"); return false; } + if (mInterfaceType == TetheringManager.TETHERING_BLUETOOTH) { + // BT configures the interface elsewhere: only start DHCP. + // TODO: make all tethering types behave the same way, and delete the bluetooth + // code that calls into NetworkManagementService directly. + return configureDhcp(enabled, mIpv4Address, null /* clientAddress */); + } + + final IpPrefix ipv4Prefix = asIpPrefix(mIpv4Address); + final Boolean setIfaceUp; if (mInterfaceType == TetheringManager.TETHERING_WIFI || mInterfaceType == TetheringManager.TETHERING_WIFI_P2P) { @@ -688,21 +672,14 @@ public class IpServer extends StateMachine { return configureDhcp(enabled, mIpv4Address, mStaticIpv4ClientAddr); } - private Inet4Address getRandomIPv4Address(@NonNull final byte[] rawAddr) { - final byte[] ipv4Addr = rawAddr; - ipv4Addr[3] = getRandomSanitizedByte(DOUG_ADAMS, asByte(0), asByte(1), FF); - try { - return (Inet4Address) InetAddress.getByAddress(ipv4Addr); - } catch (UnknownHostException e) { - mLog.e("Failed to construct Inet4Address from raw IPv4 addr"); - return null; - } - } + private LinkAddress requestIpv4Address() { + if (mStaticIpv4ServerAddr != null) return mStaticIpv4ServerAddr; - private String getRandomWifiIPv4Address() { - final Inet4Address ipv4Addr = - getRandomIPv4Address(parseNumericAddress(WIFI_HOST_IFACE_ADDR).getAddress()); - return ipv4Addr != null ? ipv4Addr.getHostAddress() : WIFI_HOST_IFACE_ADDR; + if (mInterfaceType == TetheringManager.TETHERING_BLUETOOTH) { + return new LinkAddress(BLUETOOTH_IFACE_ADDR); + } + + return mPrivateAddressCoordinator.requestDownstreamAddress(this); } private boolean startIPv6() { @@ -978,19 +955,6 @@ public class IpServer extends StateMachine { } } - // TODO: call PrivateAddressCoordinator.requestDownstreamAddress instead of this temporary - // logic. - private Inet4Address requestDownstreamAddress(@NonNull final IpPrefix currentPrefix) { - final int oldIndex = NCM_PREFIXES.indexOf(currentPrefix); - if (oldIndex == -1) { - mLog.e("current prefix isn't supported for NCM link: " + currentPrefix); - return null; - } - - final IpPrefix newPrefix = NCM_PREFIXES.get((oldIndex + 1) % NCM_PREFIXES.size()); - return getRandomIPv4Address(newPrefix.getRawAddress()); - } - private void handleNewPrefixRequest(@NonNull final IpPrefix currentPrefix) { if (!currentPrefix.contains(mIpv4Address.getAddress()) || currentPrefix.getPrefixLength() != mIpv4Address.getPrefixLength()) { @@ -999,12 +963,12 @@ public class IpServer extends StateMachine { } final LinkAddress deprecatedLinkAddress = mIpv4Address; - final Inet4Address srvAddr = requestDownstreamAddress(currentPrefix); - if (srvAddr == null) { + mIpv4Address = requestIpv4Address(); + if (mIpv4Address == null) { mLog.e("Fail to request a new downstream prefix"); return; } - mIpv4Address = new LinkAddress(srvAddr, currentPrefix.getPrefixLength()); + final Inet4Address srvAddr = (Inet4Address) mIpv4Address.getAddress(); // Add new IPv4 address on the interface. if (!mInterfaceCtrl.addAddress(srvAddr, currentPrefix.getPrefixLength())) { @@ -1162,7 +1126,7 @@ public class IpServer extends StateMachine { } try { - NetdUtils.tetherInterface(mNetd, mIfaceName, PrefixUtils.asIpPrefix(mIpv4Address)); + NetdUtils.tetherInterface(mNetd, mIfaceName, asIpPrefix(mIpv4Address)); } catch (RemoteException | ServiceSpecificException | IllegalStateException e) { mLog.e("Error Tethering", e); mLastError = TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR; @@ -1222,6 +1186,11 @@ public class IpServer extends StateMachine { case CMD_NEW_PREFIX_REQUEST: handleNewPrefixRequest((IpPrefix) message.obj); break; + case CMD_NOTIFY_PREFIX_CONFLICT: + mLog.i("restart tethering: " + mInterfaceType); + mCallback.requestEnableTethering(mInterfaceType, false /* enabled */); + transitionTo(mWaitingForRestartState); + break; default: return false; } @@ -1403,6 +1372,28 @@ public class IpServer extends StateMachine { } } + class WaitingForRestartState extends State { + @Override + public boolean processMessage(Message message) { + logMessage(this, message.what); + switch (message.what) { + case CMD_TETHER_UNREQUESTED: + transitionTo(mInitialState); + mLog.i("Untethered (unrequested) and restarting " + mIfaceName); + mCallback.requestEnableTethering(mInterfaceType, true /* enabled */); + break; + case CMD_INTERFACE_DOWN: + transitionTo(mUnavailableState); + mLog.i("Untethered (interface down) and restarting" + mIfaceName); + mCallback.requestEnableTethering(mInterfaceType, true /* enabled */); + break; + default: + return false; + } + return true; + } + } + // Accumulate routes representing "prefixes to be assigned to the local // interface", for subsequent modification of local_network routing. private static ArrayList getLocalRoutesFor( diff --git a/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java b/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java new file mode 100644 index 0000000000..160a166b63 --- /dev/null +++ b/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.networkstack.tethering; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.IpPrefix; +import android.net.LinkAddress; +import android.net.LinkProperties; +import android.net.Network; +import android.net.ip.IpServer; +import android.net.util.PrefixUtils; +import android.util.ArrayMap; +import android.util.ArraySet; + +import androidx.annotation.Nullable; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.IndentingPrintWriter; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Random; + +/** + * This class coordinate IP addresses conflict problem. + * + * Tethering downstream IP addresses may conflict with network assigned addresses. This + * coordinator is responsible for recording all of network assigned addresses and dispatched + * free address to downstream interfaces. + * + * This class is not thread-safe and should be accessed on the same tethering internal thread. + * @hide + */ +public class PrivateAddressCoordinator { + public static final int PREFIX_LENGTH = 24; + + private static final int MAX_UBYTE = 256; + private static final int BYTE_MASK = 0xff; + // reserved for bluetooth tethering. + private static final int BLUETOOTH_RESERVED = 44; + private static final byte DEFAULT_ID = (byte) 42; + + // Upstream monitor would be stopped when tethering is down. When tethering restart, downstream + // address may be requested before coordinator get current upstream notification. To ensure + // coordinator do not select conflict downstream prefix, mUpstreamPrefixMap would not be cleared + // when tethering is down. Instead coordinator would remove all depcreted upstreams from + // mUpstreamPrefixMap when tethering is starting. See #maybeRemoveDeprectedUpstreams(). + private final ArrayMap> mUpstreamPrefixMap; + private final ArraySet mDownstreams; + // IANA has reserved the following three blocks of the IP address space for private intranets: + // 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 + // Tethering use 192.168.0.0/16 that has 256 contiguous class C network numbers. + private static final String DEFAULT_TETHERING_PREFIX = "192.168.0.0/16"; + private final IpPrefix mTetheringPrefix; + private final ConnectivityManager mConnectivityMgr; + + public PrivateAddressCoordinator(Context context) { + mDownstreams = new ArraySet<>(); + mUpstreamPrefixMap = new ArrayMap<>(); + mTetheringPrefix = new IpPrefix(DEFAULT_TETHERING_PREFIX); + mConnectivityMgr = (ConnectivityManager) context.getSystemService( + Context.CONNECTIVITY_SERVICE); + } + + /** + * Record a new upstream IpPrefix which may conflict with tethering downstreams. + * The downstreams will be notified if a conflict is found. + */ + public void updateUpstreamPrefix(final Network network, final LinkProperties lp) { + final ArrayList ipv4Prefixes = getIpv4Prefixes(lp.getAllLinkAddresses()); + if (ipv4Prefixes.isEmpty()) { + removeUpstreamPrefix(network); + return; + } + + mUpstreamPrefixMap.put(network, ipv4Prefixes); + handleMaybePrefixConflict(ipv4Prefixes); + } + + private ArrayList getIpv4Prefixes(final List linkAddresses) { + final ArrayList list = new ArrayList<>(); + for (LinkAddress address : linkAddresses) { + if (!address.isIpv4()) continue; + + list.add(PrefixUtils.asIpPrefix(address)); + } + + return list; + } + + private void handleMaybePrefixConflict(final List prefixes) { + for (IpServer downstream : mDownstreams) { + final IpPrefix target = getDownstreamPrefix(downstream); + if (target == null) continue; + + for (IpPrefix source : prefixes) { + if (isConflictPrefix(source, target)) { + downstream.sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); + break; + } + } + } + } + + /** Remove IpPrefix records corresponding to input network. */ + public void removeUpstreamPrefix(final Network network) { + mUpstreamPrefixMap.remove(network); + } + + private void maybeRemoveDeprectedUpstreams() { + if (!mDownstreams.isEmpty() || mUpstreamPrefixMap.isEmpty()) return; + + final ArrayList toBeRemoved = new ArrayList<>(); + List allNetworks = Arrays.asList(mConnectivityMgr.getAllNetworks()); + for (int i = 0; i < mUpstreamPrefixMap.size(); i++) { + final Network network = mUpstreamPrefixMap.keyAt(i); + if (!allNetworks.contains(network)) toBeRemoved.add(network); + } + + mUpstreamPrefixMap.removeAll(toBeRemoved); + } + + /** + * Pick a random available address and mark its prefix as in use for the provided IpServer, + * returns null if there is no available address. + */ + @Nullable + public LinkAddress requestDownstreamAddress(final IpServer ipServer) { + maybeRemoveDeprectedUpstreams(); + + // Address would be 192.168.[subAddress]/24. + final byte[] bytes = mTetheringPrefix.getRawAddress(); + final int subAddress = getRandomSubAddr(); + final int subNet = (subAddress >> 8) & BYTE_MASK; + bytes[3] = getSanitizedAddressSuffix(subAddress, (byte) 0, (byte) 1, (byte) 0xff); + for (int i = 0; i < MAX_UBYTE; i++) { + final int newSubNet = (subNet + i) & BYTE_MASK; + if (newSubNet == BLUETOOTH_RESERVED) continue; + + bytes[2] = (byte) newSubNet; + final InetAddress addr; + try { + addr = InetAddress.getByAddress(bytes); + } catch (UnknownHostException e) { + throw new IllegalStateException("Invalid address, shouldn't happen.", e); + } + + final IpPrefix prefix = new IpPrefix(addr, PREFIX_LENGTH); + // Check whether this prefix is in use. + if (isDownstreamPrefixInUse(prefix)) continue; + // Check whether this prefix is conflict with any current upstream network. + if (isConflictWithUpstream(prefix)) continue; + + mDownstreams.add(ipServer); + return new LinkAddress(addr, PREFIX_LENGTH); + } + + // No available address. + return null; + } + + /** Get random sub address value. Return value is in 0 ~ 0xffff. */ + @VisibleForTesting + public int getRandomSubAddr() { + return ((new Random()).nextInt()) & 0xffff; // subNet is in 0 ~ 0xffff. + } + + private byte getSanitizedAddressSuffix(final int source, byte... excluded) { + final byte subId = (byte) (source & BYTE_MASK); + for (byte value : excluded) { + if (subId == value) return DEFAULT_ID; + } + + return subId; + } + + /** Release downstream record for IpServer. */ + public void releaseDownstream(final IpServer ipServer) { + mDownstreams.remove(ipServer); + } + + /** Clear current upstream prefixes records. */ + public void clearUpstreamPrefixes() { + mUpstreamPrefixMap.clear(); + } + + private boolean isConflictWithUpstream(final IpPrefix source) { + for (int i = 0; i < mUpstreamPrefixMap.size(); i++) { + final List list = mUpstreamPrefixMap.valueAt(i); + for (IpPrefix target : list) { + if (isConflictPrefix(source, target)) return true; + } + } + return false; + } + + private boolean isConflictPrefix(final IpPrefix prefix1, final IpPrefix prefix2) { + if (prefix2.getPrefixLength() < prefix1.getPrefixLength()) { + return prefix2.contains(prefix1.getAddress()); + } + + return prefix1.contains(prefix2.getAddress()); + } + + private boolean isDownstreamPrefixInUse(final IpPrefix source) { + // This class always generates downstream prefixes with the same prefix length, so + // prefixes cannot be contained in each other. They can only be equal to each other. + for (IpServer downstream : mDownstreams) { + final IpPrefix prefix = getDownstreamPrefix(downstream); + if (source.equals(prefix)) return true; + } + return false; + } + + private IpPrefix getDownstreamPrefix(final IpServer downstream) { + final LinkAddress address = downstream.getAddress(); + if (address == null) return null; + + return PrefixUtils.asIpPrefix(address); + } + + void dump(final IndentingPrintWriter pw) { + pw.decreaseIndent(); + pw.println("mUpstreamPrefixMap:"); + pw.increaseIndent(); + for (int i = 0; i < mUpstreamPrefixMap.size(); i++) { + pw.println(mUpstreamPrefixMap.keyAt(i) + " - " + mUpstreamPrefixMap.valueAt(i)); + } + pw.decreaseIndent(); + pw.println("mDownstreams:"); + pw.increaseIndent(); + for (IpServer ipServer : mDownstreams) { + pw.println(ipServer.interfaceType() + " - " + ipServer.getAddress()); + } + pw.decreaseIndent(); + } +} diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java index 04ad43f6e2..69eec8df98 100644 --- a/Tethering/src/com/android/networkstack/tethering/Tethering.java +++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java @@ -207,6 +207,7 @@ public class Tethering { new SparseArray<>(); // used to synchronize public access to members + // TODO(b/153621704): remove mPublicSync to make Tethering lock free private final Object mPublicSync; private final Context mContext; private final ArrayMap mTetherStates; @@ -231,6 +232,7 @@ public class Tethering { private final TetheringThreadExecutor mExecutor; private final TetheringNotificationUpdater mNotificationUpdater; private final UserManager mUserManager; + private final PrivateAddressCoordinator mPrivateAddressCoordinator; private int mActiveDataSubId = INVALID_SUBSCRIPTION_ID; // All the usage of mTetheringEventCallback should run in the same thread. private ITetheringEventCallback mTetheringEventCallback = null; @@ -314,6 +316,7 @@ public class Tethering { mExecutor = new TetheringThreadExecutor(mHandler); mActiveDataSubIdListener = new ActiveDataSubIdListener(mExecutor); mNetdCallback = new NetdCallback(); + mPrivateAddressCoordinator = new PrivateAddressCoordinator(mContext); // Load tethering configuration. updateConfiguration(); @@ -1616,6 +1619,14 @@ public class Tethering { } } + private void addUpstreamPrefixes(final UpstreamNetworkState ns) { + mPrivateAddressCoordinator.updateUpstreamPrefix(ns.network, ns.linkProperties); + } + + private void removeUpstreamPrefixes(final UpstreamNetworkState ns) { + mPrivateAddressCoordinator.removeUpstreamPrefix(ns.network); + } + @VisibleForTesting void handleUpstreamNetworkMonitorCallback(int arg1, Object o) { if (arg1 == UpstreamNetworkMonitor.NOTIFY_LOCAL_PREFIXES) { @@ -1624,6 +1635,14 @@ public class Tethering { } final UpstreamNetworkState ns = (UpstreamNetworkState) o; + switch (arg1) { + case UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES: + addUpstreamPrefixes(ns); + break; + case UpstreamNetworkMonitor.EVENT_ON_LOST: + removeUpstreamPrefixes(ns); + break; + } if (ns == null || !pertainsToCurrentUpstream(ns)) { // TODO: In future, this is where upstream evaluation and selection @@ -2190,6 +2209,11 @@ public class Tethering { mOffloadController.dump(pw); pw.decreaseIndent(); + pw.println("Private address coordinator:"); + pw.increaseIndent(); + mPrivateAddressCoordinator.dump(pw); + pw.decreaseIndent(); + pw.println("Log:"); pw.increaseIndent(); if (argsContain(args, "--short")) { @@ -2231,6 +2255,11 @@ public class Tethering { public void dhcpLeasesChanged() { updateConnectedClients(null /* wifiClients */); } + + @Override + public void requestEnableTethering(int tetheringType, boolean enabled) { + enableTetheringInternal(tetheringType, enabled, null); + } }; } @@ -2314,7 +2343,8 @@ public class Tethering { final TetherState tetherState = new TetherState( new IpServer(iface, mLooper, interfaceType, mLog, mNetd, makeControlCallback(), mConfig.enableLegacyDhcpServer, - mConfig.enableBpfOffload, mDeps.getIpServerDependencies())); + mConfig.enableBpfOffload, mPrivateAddressCoordinator, + mDeps.getIpServerDependencies())); mTetherStates.put(iface, tetherState); tetherState.ipServer.start(); } diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java index 307ebf17d2..0cda29a32f 100644 --- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java +++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java @@ -78,6 +78,7 @@ import android.net.ip.IpNeighborMonitor.NeighborEventConsumer; import android.net.ip.RouterAdvertisementDaemon.RaParams; import android.net.util.InterfaceParams; import android.net.util.InterfaceSet; +import android.net.util.PrefixUtils; import android.net.util.SharedLog; import android.os.RemoteException; import android.os.test.TestLooper; @@ -86,6 +87,8 @@ import android.text.TextUtils; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.networkstack.tethering.PrivateAddressCoordinator; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -109,7 +112,7 @@ public class IpServerTest { private static final String UPSTREAM_IFACE2 = "upstream1"; private static final int UPSTREAM_IFINDEX = 101; private static final int UPSTREAM_IFINDEX2 = 102; - private static final String BLUETOOTH_IFACE_ADDR = "192.168.42.1"; + private static final String BLUETOOTH_IFACE_ADDR = "192.168.44.1"; private static final int BLUETOOTH_DHCP_PREFIX_LENGTH = 24; private static final int DHCP_LEASE_TIME_SECS = 3600; private static final boolean DEFAULT_USING_BPF_OFFLOAD = true; @@ -119,6 +122,9 @@ public class IpServerTest { private static final int MAKE_DHCPSERVER_TIMEOUT_MS = 1000; + private final LinkAddress mTestAddress = new LinkAddress("192.168.42.5/24"); + private final IpPrefix mBluetoothPrefix = new IpPrefix("192.168.44.0/24"); + @Mock private INetd mNetd; @Mock private IpServer.Callback mCallback; @Mock private SharedLog mSharedLog; @@ -126,6 +132,7 @@ public class IpServerTest { @Mock private RouterAdvertisementDaemon mRaDaemon; @Mock private IpNeighborMonitor mIpNeighborMonitor; @Mock private IpServer.Dependencies mDependencies; + @Mock private PrivateAddressCoordinator mAddressCoordinator; @Captor private ArgumentCaptor mDhcpParamsCaptor; @@ -173,7 +180,7 @@ public class IpServerTest { mIpServer = new IpServer( IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd, - mCallback, usingLegacyDhcp, usingBpfOffload, mDependencies); + mCallback, usingLegacyDhcp, usingBpfOffload, mAddressCoordinator, mDependencies); mIpServer.start(); mNeighborEventConsumer = neighborCaptor.getValue(); @@ -200,12 +207,14 @@ public class IpServerTest { lp.setInterfaceName(upstreamIface); dispatchTetherConnectionChanged(upstreamIface, lp, 0); } - reset(mNetd, mCallback); + reset(mNetd, mCallback, mAddressCoordinator); + when(mAddressCoordinator.requestDownstreamAddress(any())).thenReturn(mTestAddress); } @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); when(mSharedLog.forSubComponent(anyString())).thenReturn(mSharedLog); + when(mAddressCoordinator.requestDownstreamAddress(any())).thenReturn(mTestAddress); } @Test @@ -214,7 +223,7 @@ public class IpServerTest { .thenReturn(mIpNeighborMonitor); mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), TETHERING_BLUETOOTH, mSharedLog, mNetd, mCallback, false /* usingLegacyDhcp */, DEFAULT_USING_BPF_OFFLOAD, - mDependencies); + mAddressCoordinator, mDependencies); mIpServer.start(); mLooper.dispatchAll(); verify(mCallback).updateInterfaceState( @@ -277,16 +286,17 @@ public class IpServerTest { initTetheredStateMachine(TETHERING_BLUETOOTH, null); dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED); - InOrder inOrder = inOrder(mNetd, mCallback); + InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator); inOrder.verify(mNetd).tetherApplyDnsInterfaces(); inOrder.verify(mNetd).tetherInterfaceRemove(IFACE_NAME); inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME); inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName))); + inOrder.verify(mAddressCoordinator).releaseDownstream(any()); inOrder.verify(mCallback).updateInterfaceState( mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR); inOrder.verify(mCallback).updateLinkProperties( eq(mIpServer), any(LinkProperties.class)); - verifyNoMoreInteractions(mNetd, mCallback); + verifyNoMoreInteractions(mNetd, mCallback, mAddressCoordinator); } @Test @@ -294,7 +304,8 @@ public class IpServerTest { initStateMachine(TETHERING_USB); dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED); - InOrder inOrder = inOrder(mCallback, mNetd); + InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator); + inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any()); inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName) && assertContainsFlag(cfg.flags, IF_STATE_UP))); inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME); @@ -306,7 +317,7 @@ public class IpServerTest { inOrder.verify(mCallback).updateLinkProperties( eq(mIpServer), mLinkPropertiesCaptor.capture()); assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue()); - verifyNoMoreInteractions(mNetd, mCallback); + verifyNoMoreInteractions(mNetd, mCallback, mAddressCoordinator); } @Test @@ -314,7 +325,8 @@ public class IpServerTest { initStateMachine(TETHERING_WIFI_P2P); dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_LOCAL_ONLY); - InOrder inOrder = inOrder(mCallback, mNetd); + InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator); + inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any()); inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName) && assertNotContainsFlag(cfg.flags, IF_STATE_UP))); inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME); @@ -326,7 +338,7 @@ public class IpServerTest { inOrder.verify(mCallback).updateLinkProperties( eq(mIpServer), mLinkPropertiesCaptor.capture()); assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue()); - verifyNoMoreInteractions(mNetd, mCallback); + verifyNoMoreInteractions(mNetd, mCallback, mAddressCoordinator); } @Test @@ -392,18 +404,19 @@ public class IpServerTest { initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE); dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED); - InOrder inOrder = inOrder(mNetd, mCallback); + InOrder inOrder = inOrder(mNetd, mCallback, mAddressCoordinator); inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE); inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE); inOrder.verify(mNetd).tetherApplyDnsInterfaces(); inOrder.verify(mNetd).tetherInterfaceRemove(IFACE_NAME); inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME); inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName))); + inOrder.verify(mAddressCoordinator).releaseDownstream(any()); inOrder.verify(mCallback).updateInterfaceState( mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR); inOrder.verify(mCallback).updateLinkProperties( eq(mIpServer), any(LinkProperties.class)); - verifyNoMoreInteractions(mNetd, mCallback); + verifyNoMoreInteractions(mNetd, mCallback, mAddressCoordinator); } @Test @@ -483,7 +496,7 @@ public class IpServerTest { initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE); dispatchTetherConnectionChanged(UPSTREAM_IFACE); - assertDhcpStarted(new IpPrefix("192.168.43.0/24")); + assertDhcpStarted(PrefixUtils.asIpPrefix(mTestAddress)); } @Test @@ -491,7 +504,7 @@ public class IpServerTest { initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE); dispatchTetherConnectionChanged(UPSTREAM_IFACE); - assertDhcpStarted(new IpPrefix("192.168.44.0/24")); + assertDhcpStarted(mBluetoothPrefix); } @Test @@ -499,7 +512,7 @@ public class IpServerTest { initTetheredStateMachine(TETHERING_WIFI_P2P, UPSTREAM_IFACE); dispatchTetherConnectionChanged(UPSTREAM_IFACE); - assertDhcpStarted(new IpPrefix("192.168.49.0/24")); + assertDhcpStarted(PrefixUtils.asIpPrefix(mTestAddress)); } @Test @@ -524,21 +537,27 @@ public class IpServerTest { eventCallbacks = dhcpEventCbsCaptor.getValue(); assertDhcpStarted(new IpPrefix("192.168.42.0/24")); - // Simulate the DHCP server receives DHCPDECLINE on MirrorLink and then signals - // onNewPrefixRequest callback. - eventCallbacks.onNewPrefixRequest(new IpPrefix("192.168.42.0/24")); - mLooper.dispatchAll(); - final ArgumentCaptor lpCaptor = ArgumentCaptor.forClass(LinkProperties.class); - InOrder inOrder = inOrder(mNetd, mCallback); - inOrder.verify(mCallback).updateInterfaceState( - mIpServer, STATE_LOCAL_ONLY, TETHER_ERROR_NO_ERROR); - inOrder.verify(mCallback).updateLinkProperties(eq(mIpServer), lpCaptor.capture()); + InOrder inOrder = inOrder(mNetd, mCallback, mAddressCoordinator); + inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any()); inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME); // One for ipv4 route, one for ipv6 link local route. inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME), any(), any()); + inOrder.verify(mCallback).updateInterfaceState( + mIpServer, STATE_LOCAL_ONLY, TETHER_ERROR_NO_ERROR); + inOrder.verify(mCallback).updateLinkProperties(eq(mIpServer), lpCaptor.capture()); + verifyNoMoreInteractions(mCallback, mAddressCoordinator); + + // Simulate the DHCP server receives DHCPDECLINE on MirrorLink and then signals + // onNewPrefixRequest callback. + final LinkAddress newAddress = new LinkAddress("192.168.100.125/24"); + when(mAddressCoordinator.requestDownstreamAddress(any())).thenReturn(newAddress); + eventCallbacks.onNewPrefixRequest(new IpPrefix("192.168.42.0/24")); + mLooper.dispatchAll(); + + inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any()); inOrder.verify(mNetd).tetherApplyDnsInterfaces(); inOrder.verify(mCallback).updateLinkProperties(eq(mIpServer), lpCaptor.capture()); verifyNoMoreInteractions(mCallback); diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java new file mode 100644 index 0000000000..93efd49a6d --- /dev/null +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.networkstack.tethering; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.InetAddresses; +import android.net.IpPrefix; +import android.net.LinkAddress; +import android.net.LinkProperties; +import android.net.Network; +import android.net.ip.IpServer; +import android.net.util.NetworkConstants; +import android.net.util.PrefixUtils; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.List; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public final class PrivateAddressCoordinatorTest { + private static final String TEST_MOBILE_IFNAME = "test_rmnet_data0"; + private static final String TEST_WIFI_IFNAME = "test_wlan0"; + + @Mock private IpServer mHotspotIpServer; + @Mock private IpServer mUsbIpServer; + @Mock private IpServer mEthernetIpServer; + @Mock private Context mContext; + @Mock private ConnectivityManager mConnectivityMgr; + + private PrivateAddressCoordinator mPrivateAddressCoordinator; + private final IpPrefix mBluetoothPrefix = new IpPrefix("192.168.44.0/24"); + private final Network mWifiNetwork = new Network(1); + private final Network mMobileNetwork = new Network(2); + private final Network[] mAllNetworks = {mMobileNetwork, mWifiNetwork}; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(mConnectivityMgr); + when(mConnectivityMgr.getAllNetworks()).thenReturn(mAllNetworks); + mPrivateAddressCoordinator = spy(new PrivateAddressCoordinator(mContext)); + } + + @Test + public void testDownstreamPrefixRequest() throws Exception { + LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress( + mHotspotIpServer); + final IpPrefix hotspotPrefix = PrefixUtils.asIpPrefix(address); + assertNotEquals(hotspotPrefix, mBluetoothPrefix); + + address = mPrivateAddressCoordinator.requestDownstreamAddress( + mHotspotIpServer); + final IpPrefix testDupRequest = PrefixUtils.asIpPrefix(address); + assertNotEquals(hotspotPrefix, testDupRequest); + assertNotEquals(mBluetoothPrefix, testDupRequest); + mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); + + address = mPrivateAddressCoordinator.requestDownstreamAddress( + mUsbIpServer); + final IpPrefix usbPrefix = PrefixUtils.asIpPrefix(address); + assertNotEquals(usbPrefix, mBluetoothPrefix); + assertNotEquals(usbPrefix, hotspotPrefix); + mPrivateAddressCoordinator.releaseDownstream(mUsbIpServer); + } + + @Test + public void testRequestDownstreamAddress() throws Exception { + LinkAddress expectedAddress = new LinkAddress("192.168.43.42/24"); + int fakeSubAddr = 0x2b00; + when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeSubAddr); + LinkAddress actualAddress = mPrivateAddressCoordinator.requestDownstreamAddress( + mHotspotIpServer); + assertEquals(actualAddress, expectedAddress); + mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); + + fakeSubAddr = 0x2b01; + when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeSubAddr); + actualAddress = mPrivateAddressCoordinator.requestDownstreamAddress( + mHotspotIpServer); + assertEquals(actualAddress, expectedAddress); + mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); + + fakeSubAddr = 0x2bff; + when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeSubAddr); + actualAddress = mPrivateAddressCoordinator.requestDownstreamAddress( + mHotspotIpServer); + assertEquals(actualAddress, expectedAddress); + mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); + + expectedAddress = new LinkAddress("192.168.43.5/24"); + fakeSubAddr = 0x2b05; + when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeSubAddr); + actualAddress = mPrivateAddressCoordinator.requestDownstreamAddress( + mHotspotIpServer); + assertEquals(actualAddress, expectedAddress); + mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); + } + + @Test + public void testReserveBluetoothPrefix() throws Exception { + final int fakeSubAddr = 0x2c05; + when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeSubAddr); + LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress( + mHotspotIpServer); + final IpPrefix hotspotPrefix = PrefixUtils.asIpPrefix(address); + assertNotEquals("Should not get reserved prefix: ", mBluetoothPrefix, hotspotPrefix); + mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); + } + + @Test + public void testNoConflictDownstreamPrefix() throws Exception { + final int fakeHotspotSubAddr = 0x2b05; + final IpPrefix predefinedPrefix = new IpPrefix("192.168.43.0/24"); + when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeHotspotSubAddr); + LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress( + mHotspotIpServer); + final IpPrefix hotspotPrefix = PrefixUtils.asIpPrefix(address); + assertEquals("Wrong wifi perfix: ", predefinedPrefix, hotspotPrefix); + when(mHotspotIpServer.getAddress()).thenReturn(address); + + address = mPrivateAddressCoordinator.requestDownstreamAddress( + mUsbIpServer); + final IpPrefix usbPrefix = PrefixUtils.asIpPrefix(address); + assertNotEquals(predefinedPrefix, usbPrefix); + + mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); + mPrivateAddressCoordinator.releaseDownstream(mUsbIpServer); + address = mPrivateAddressCoordinator.requestDownstreamAddress( + mUsbIpServer); + final IpPrefix allowUseFreePrefix = PrefixUtils.asIpPrefix(address); + assertEquals("Fail to reselect available perfix: ", predefinedPrefix, allowUseFreePrefix); + } + + private LinkProperties buildUpstreamLinkProperties(boolean withIPv4, boolean withIPv6, + boolean isMobile) { + final String testIface; + final String testIpv4Address; + if (isMobile) { + testIface = TEST_MOBILE_IFNAME; + testIpv4Address = "10.0.0.1"; + } else { + testIface = TEST_WIFI_IFNAME; + testIpv4Address = "192.168.43.5"; + } + + final LinkProperties prop = new LinkProperties(); + prop.setInterfaceName(testIface); + + if (withIPv4) { + prop.addLinkAddress( + new LinkAddress(InetAddresses.parseNumericAddress(testIpv4Address), + NetworkConstants.IPV4_ADDR_BITS)); + } + + if (withIPv6) { + prop.addLinkAddress( + new LinkAddress(InetAddresses.parseNumericAddress("2001:db8::"), + NetworkConstants.RFC7421_PREFIX_LENGTH)); + } + return prop; + } + + @Test + public void testNoConflictUpstreamPrefix() throws Exception { + final int fakeHotspotSubId = 43; + final int fakeHotspotSubAddr = 0x2b05; + final IpPrefix predefinedPrefix = new IpPrefix("192.168.43.0/24"); + // Force always get subAddress "43.5" for conflict testing. + when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeHotspotSubAddr); + // 1. Enable hotspot with prefix 192.168.43.0/24 + final LinkAddress hotspotAddr = mPrivateAddressCoordinator.requestDownstreamAddress( + mHotspotIpServer); + final IpPrefix hotspotPrefix = PrefixUtils.asIpPrefix(hotspotAddr); + assertEquals("Wrong wifi perfix: ", predefinedPrefix, hotspotPrefix); + when(mHotspotIpServer.getAddress()).thenReturn(hotspotAddr); + // 2. Update v6 only mobile network, hotspot prefix should not be removed. + List testConflicts; + final LinkProperties v6OnlyMobileProp = buildUpstreamLinkProperties(false, true, true); + mPrivateAddressCoordinator.updateUpstreamPrefix(mMobileNetwork, v6OnlyMobileProp); + verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); + mPrivateAddressCoordinator.removeUpstreamPrefix(mMobileNetwork); + // 3. Update v4 only mobile network, hotspot prefix should not be removed. + final LinkProperties v4OnlyMobileProp = buildUpstreamLinkProperties(true, false, true); + mPrivateAddressCoordinator.updateUpstreamPrefix(mMobileNetwork, v4OnlyMobileProp); + verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); + // 4. Update v4v6 mobile network, hotspot prefix should not be removed. + final LinkProperties v4v6MobileProp = buildUpstreamLinkProperties(true, true, true); + mPrivateAddressCoordinator.updateUpstreamPrefix(mMobileNetwork, v4v6MobileProp); + verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); + // 5. Update v6 only wifi network, hotspot prefix should not be removed. + final LinkProperties v6OnlyWifiProp = buildUpstreamLinkProperties(false, true, false); + mPrivateAddressCoordinator.updateUpstreamPrefix(mWifiNetwork, v6OnlyWifiProp); + verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); + mPrivateAddressCoordinator.removeUpstreamPrefix(mWifiNetwork); + // 6. Update v4 only wifi network, it conflict with hotspot prefix. + final LinkProperties v4OnlyWifiProp = buildUpstreamLinkProperties(true, false, false); + mPrivateAddressCoordinator.updateUpstreamPrefix(mWifiNetwork, v4OnlyWifiProp); + verify(mHotspotIpServer).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); + reset(mHotspotIpServer); + // 7. Restart hotspot again and its prefix is different previous. + mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); + final LinkAddress hotspotAddr2 = mPrivateAddressCoordinator.requestDownstreamAddress( + mHotspotIpServer); + final IpPrefix hotspotPrefix2 = PrefixUtils.asIpPrefix(hotspotAddr2); + assertNotEquals(hotspotPrefix, hotspotPrefix2); + when(mHotspotIpServer.getAddress()).thenReturn(hotspotAddr2); + mPrivateAddressCoordinator.updateUpstreamPrefix(mWifiNetwork, v4OnlyWifiProp); + verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); + // 7. Usb tethering can be enabled and its prefix is different with conflict one. + final LinkAddress usbAddr = mPrivateAddressCoordinator.requestDownstreamAddress( + mUsbIpServer); + final IpPrefix usbPrefix = PrefixUtils.asIpPrefix(usbAddr); + assertNotEquals(predefinedPrefix, usbPrefix); + assertNotEquals(hotspotPrefix2, usbPrefix); + when(mUsbIpServer.getAddress()).thenReturn(usbAddr); + // 8. Disable wifi upstream, then wifi's prefix can be selected again. + mPrivateAddressCoordinator.removeUpstreamPrefix(mWifiNetwork); + final LinkAddress ethAddr = mPrivateAddressCoordinator.requestDownstreamAddress( + mEthernetIpServer); + final IpPrefix ethPrefix = PrefixUtils.asIpPrefix(ethAddr); + assertEquals(predefinedPrefix, ethPrefix); + } +} 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 0132aba0b7..bb65b18edb 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java @@ -32,6 +32,7 @@ import static android.net.TetheringManager.TETHERING_ETHERNET; import static android.net.TetheringManager.TETHERING_NCM; import static android.net.TetheringManager.TETHERING_USB; import static android.net.TetheringManager.TETHERING_WIFI; +import static android.net.TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR; import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_IFACE; import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_FAILED; @@ -84,6 +85,7 @@ import android.content.res.Resources; import android.hardware.usb.UsbManager; import android.net.ConnectivityManager; import android.net.EthernetManager; +import android.net.EthernetManager.TetheredInterfaceCallback; import android.net.EthernetManager.TetheredInterfaceRequest; import android.net.IIntResultListener; import android.net.INetd; @@ -169,9 +171,11 @@ public class TetheringTest { private static final String TEST_MOBILE_IFNAME = "test_rmnet_data0"; private static final String TEST_XLAT_MOBILE_IFNAME = "v4-test_rmnet_data0"; private static final String TEST_USB_IFNAME = "test_rndis0"; - private static final String TEST_WLAN_IFNAME = "test_wlan0"; + private static final String TEST_WIFI_IFNAME = "test_wlan0"; + private static final String TEST_WLAN_IFNAME = "test_wlan1"; private static final String TEST_P2P_IFNAME = "test_p2p-p2p0-0"; private static final String TEST_NCM_IFNAME = "test_ncm0"; + private static final String TEST_ETH_IFNAME = "test_eth0"; private static final String TETHERING_NAME = "Tethering"; private static final String[] PROVISIONING_APP_NAME = {"some", "app"}; private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app"; @@ -279,10 +283,11 @@ public class TetheringTest { || ifName.equals(TEST_WLAN_IFNAME) || ifName.equals(TEST_MOBILE_IFNAME) || ifName.equals(TEST_P2P_IFNAME) - || ifName.equals(TEST_NCM_IFNAME)); + || ifName.equals(TEST_NCM_IFNAME) + || ifName.equals(TEST_ETH_IFNAME)); final String[] ifaces = new String[] { TEST_USB_IFNAME, TEST_WLAN_IFNAME, TEST_MOBILE_IFNAME, TEST_P2P_IFNAME, - TEST_NCM_IFNAME}; + TEST_NCM_IFNAME, TEST_ETH_IFNAME}; return new InterfaceParams(ifName, ArrayUtils.indexOf(ifaces, ifName) + IFINDEX_OFFSET, MacAddress.ALL_ZEROS_ADDRESS); } @@ -490,7 +495,7 @@ public class TetheringTest { when(mNetd.interfaceGetList()) .thenReturn(new String[] { TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME, - TEST_NCM_IFNAME}); + TEST_NCM_IFNAME, TEST_ETH_IFNAME}); when(mResources.getString(R.string.config_wifi_tether_enable)).thenReturn(""); mInterfaceConfiguration = new InterfaceConfigurationParcel(); mInterfaceConfiguration.flags = new String[0]; @@ -1836,6 +1841,109 @@ public class TetheringTest { mCarrierConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true); sendConfigurationChanged(); } + + private static UpstreamNetworkState buildV4WifiUpstreamState(final String ipv4Address, + final int prefixLength, final Network network) { + final LinkProperties prop = new LinkProperties(); + prop.setInterfaceName(TEST_WIFI_IFNAME); + + prop.addLinkAddress( + new LinkAddress(InetAddresses.parseNumericAddress(ipv4Address), + prefixLength)); + + final NetworkCapabilities capabilities = new NetworkCapabilities() + .addTransportType(NetworkCapabilities.TRANSPORT_WIFI); + return new UpstreamNetworkState(prop, capabilities, network); + } + + @Test + public void testHandleIpConflict() throws Exception { + final Network wifiNetwork = new Network(200); + final Network[] allNetworks = { wifiNetwork }; + when(mCm.getAllNetworks()).thenReturn(allNetworks); + UpstreamNetworkState upstreamNetwork = null; + runUsbTethering(upstreamNetwork); + final ArgumentCaptor ifaceConfigCaptor = + ArgumentCaptor.forClass(InterfaceConfigurationParcel.class); + verify(mNetd).interfaceSetCfg(ifaceConfigCaptor.capture()); + final String ipv4Address = ifaceConfigCaptor.getValue().ipv4Addr; + verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks( + any(), any()); + reset(mNetd, mUsbManager); + upstreamNetwork = buildV4WifiUpstreamState(ipv4Address, 30, wifiNetwork); + mTetheringDependencies.mUpstreamNetworkMonitorMasterSM.sendMessage( + Tethering.TetherMasterSM.EVENT_UPSTREAM_CALLBACK, + UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES, + 0, + upstreamNetwork); + mLooper.dispatchAll(); + // verify trun off usb tethering + verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_NONE); + mTethering.interfaceRemoved(TEST_USB_IFNAME); + mLooper.dispatchAll(); + // verify restart usb tethering + verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_RNDIS); + } + + @Test + public void testNoAddressAvailable() throws Exception { + final Network wifiNetwork = new Network(200); + final Network[] allNetworks = { wifiNetwork }; + when(mCm.getAllNetworks()).thenReturn(allNetworks); + final String upstreamAddress = "192.168.0.100"; + runUsbTethering(null); + verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks( + any(), any()); + reset(mUsbManager); + final TetheredInterfaceRequest mockRequest = mock(TetheredInterfaceRequest.class); + when(mEm.requestTetheredInterface(any(), any())).thenReturn(mockRequest); + final ArgumentCaptor callbackCaptor = + ArgumentCaptor.forClass(TetheredInterfaceCallback.class); + mTethering.startTethering(createTetheringRequestParcel(TETHERING_ETHERNET), null); + mLooper.dispatchAll(); + verify(mEm).requestTetheredInterface(any(), callbackCaptor.capture()); + TetheredInterfaceCallback ethCallback = callbackCaptor.getValue(); + ethCallback.onAvailable(TEST_ETH_IFNAME); + mLooper.dispatchAll(); + reset(mUsbManager, mEm); + + final UpstreamNetworkState upstreamNetwork = buildV4WifiUpstreamState( + upstreamAddress, 16, wifiNetwork); + mTetheringDependencies.mUpstreamNetworkMonitorMasterSM.sendMessage( + Tethering.TetherMasterSM.EVENT_UPSTREAM_CALLBACK, + UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES, + 0, + upstreamNetwork); + mLooper.dispatchAll(); + // verify trun off usb tethering + verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_NONE); + // verify trun off ethernet tethering + verify(mockRequest).release(); + mTethering.interfaceRemoved(TEST_USB_IFNAME); + ethCallback.onUnavailable(); + mLooper.dispatchAll(); + // verify restart usb tethering + verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_RNDIS); + // verify restart ethernet tethering + verify(mEm).requestTetheredInterface(any(), callbackCaptor.capture()); + ethCallback = callbackCaptor.getValue(); + ethCallback.onAvailable(TEST_ETH_IFNAME); + + reset(mUsbManager, mEm); + when(mNetd.interfaceGetList()) + .thenReturn(new String[] { + TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME, + TEST_NCM_IFNAME, TEST_ETH_IFNAME}); + + mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true); + sendUsbBroadcast(true, true, true, TETHERING_USB); + mLooper.dispatchAll(); + assertContains(Arrays.asList(mTethering.getTetherableIfaces()), TEST_USB_IFNAME); + assertContains(Arrays.asList(mTethering.getTetherableIfaces()), TEST_ETH_IFNAME); + assertEquals(TETHER_ERROR_IFACE_CFG_ERROR, mTethering.getLastTetherError(TEST_USB_IFNAME)); + assertEquals(TETHER_ERROR_IFACE_CFG_ERROR, mTethering.getLastTetherError(TEST_ETH_IFNAME)); + } + // TODO: Test that a request for hotspot mode doesn't interfere with an // already operating tethering mode interface. } From a17cf677b5fe58ddec610ffd92f7b1b378785232 Mon Sep 17 00:00:00 2001 From: Paul Duffin Date: Sun, 31 May 2020 11:32:06 +0100 Subject: [PATCH 151/188] Clean up the visibility rules for framework modules Switching from java_library to java_sdk_library switched the meaning of the module name from referring to the implementation library to referring to the stubs. This change updates the visibility rules to reflect that new meaning. Visibility rules that were previously set for the java_library have been moved to the impl_library_visibility property and the special //visibility:override value has been prepended to prevent it from inheriting the values from the visibility property. Visibility rules set for the stubs (via stubs_library_visibility) property have been moved to the visibility property. Bug: 155164730 Test: m nothing Exempt-From-Owner-Approval: Build cleanup Change-Id: Icc9bc5a9ef86cf7ba0f15c2b2a4abd596ec9f640 --- Tethering/common/TetheringLib/Android.bp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Tethering/common/TetheringLib/Android.bp b/Tethering/common/TetheringLib/Android.bp index 408725c865..6c05b11258 100644 --- a/Tethering/common/TetheringLib/Android.bp +++ b/Tethering/common/TetheringLib/Android.bp @@ -16,6 +16,16 @@ java_sdk_library { name: "framework-tethering", defaults: ["framework-module-defaults"], + + // Allow access to the stubs from anywhere. + visibility: ["//visibility:public"], + + // Restrict access to implementation library. + impl_library_visibility: [ + "//visibility:override", // Ignore the visibility property. + "//frameworks/base/packages/Tethering:__subpackages__", + ], + srcs: [ ":framework-tethering-srcs", ], @@ -29,8 +39,6 @@ java_sdk_library { installable: true, hostdex: true, // for hiddenapi check - visibility: ["//frameworks/base/packages/Tethering:__subpackages__"], - stubs_library_visibility: ["//visibility:public"], apex_available: ["com.android.tethering"], permitted_packages: ["android.net"], } From 03b52fcfd509e2f85c04534275726acf91a7feca Mon Sep 17 00:00:00 2001 From: markchien Date: Fri, 29 May 2020 14:36:36 +0800 Subject: [PATCH 152/188] Gate exemptFromEentitlementCheck by Network_STACK permission Shell has TETHER_PRIVILEGED permission. To avoid any service to adopt shell identity by lunching service with Shell process, gate exemptFromEentitlementCheck by NETWORK_STACK. Bug: 157702014 Test: atest TetheringCoverageTests Change-Id: I6ddfda23d36ea9981e3e1eb5a87767f452a65852 Merged-In: I6ddfda23d36ea9981e3e1eb5a87767f452a65852 --- .../tethering/TetheringService.java | 17 ++++++++++++-- .../android/net/EthernetTetheringTest.java | 4 ++-- .../tethering/TetheringServiceTest.java | 23 ++++++++++++++----- 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringService.java b/Tethering/src/com/android/networkstack/tethering/TetheringService.java index e095afea52..d084ca0966 100644 --- a/Tethering/src/com/android/networkstack/tethering/TetheringService.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringService.java @@ -17,8 +17,10 @@ package com.android.networkstack.tethering; import static android.Manifest.permission.ACCESS_NETWORK_STATE; +import static android.Manifest.permission.NETWORK_STACK; import static android.Manifest.permission.TETHER_PRIVILEGED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; import static android.net.TetheringManager.TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION; import static android.net.TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION; import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; @@ -240,15 +242,26 @@ public class TetheringService extends Service { return false; } + private boolean hasNetworkStackPermission() { + return checkCallingOrSelfPermission(NETWORK_STACK) + || checkCallingOrSelfPermission(PERMISSION_MAINLINE_NETWORK_STACK); + } + private boolean hasTetherPrivilegedPermission() { - return mService.checkCallingOrSelfPermission(TETHER_PRIVILEGED) == PERMISSION_GRANTED; + return checkCallingOrSelfPermission(TETHER_PRIVILEGED); + } + + private boolean checkCallingOrSelfPermission(final String permission) { + return mService.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED; } private boolean hasTetherChangePermission(final String callerPkg, final boolean onlyAllowPrivileged) { + if (onlyAllowPrivileged && !hasNetworkStackPermission()) return false; + if (hasTetherPrivilegedPermission()) return true; - if (onlyAllowPrivileged || mTethering.isTetherProvisioningRequired()) return false; + if (mTethering.isTetherProvisioningRequired()) return false; int uid = Binder.getCallingUid(); // If callerPkg's uid is not same as Binder.getCallingUid(), diff --git a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java index 2fb7e607d0..74df11370e 100644 --- a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java +++ b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java @@ -339,7 +339,7 @@ public class EthernetTetheringTest { private MyTetheringEventCallback enableEthernetTethering(String iface) throws Exception { return enableEthernetTethering(iface, new TetheringRequest.Builder(TETHERING_ETHERNET) - .setExemptFromEntitlementCheck(true).build()); + .setShouldShowEntitlementUi(false).build()); } private int getMTU(TestNetworkInterface iface) throws SocketException { @@ -510,7 +510,7 @@ public class EthernetTetheringTest { LinkAddress clientAddr = client == null ? null : new LinkAddress(client); return new TetheringRequest.Builder(TETHERING_ETHERNET) .setStaticIpv4Addresses(localAddr, clientAddr) - .setExemptFromEntitlementCheck(true).build(); + .setShouldShowEntitlementUi(false).build(); } private void assertInvalidStaticIpv4Request(String iface, String local, String client) diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java index f4a5666598..22d894bf47 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java @@ -274,21 +274,32 @@ public final class TetheringServiceTest { }); } + private void runStartTetheringAndVerifyNoPermission(final TestTetheringResult result) + throws Exception { + final TetheringRequestParcel request = new TetheringRequestParcel(); + request.tetheringType = TETHERING_WIFI; + request.exemptFromEntitlementCheck = true; + mTetheringConnector.startTethering(request, TEST_CALLER_PKG, result); + result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); + verifyNoMoreInteractionsForTethering(); + } + @Test - public void testStartTetheringWithExemptFromEntitlementCheck() throws Exception { + public void testFailToBypassEntitlementWithoutNeworkStackPermission() throws Exception { final TetheringRequestParcel request = new TetheringRequestParcel(); request.tetheringType = TETHERING_WIFI; request.exemptFromEntitlementCheck = true; + runAsNoPermission((result) -> { + runStartTetheringAndVerifyNoPermission(result); + }); + runAsTetherPrivileged((result) -> { - runStartTethering(result, request); - verifyNoMoreInteractionsForTethering(); + runStartTetheringAndVerifyNoPermission(result); }); runAsWriteSettings((result) -> { - mTetheringConnector.startTethering(request, TEST_CALLER_PKG, result); - result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); - verifyNoMoreInteractionsForTethering(); + runStartTetheringAndVerifyNoPermission(result); }); } From 6812c1b1eedeca9cb25054e0c5f33269ae4fa0cf Mon Sep 17 00:00:00 2001 From: Paul Duffin Date: Sun, 31 May 2020 11:35:50 +0100 Subject: [PATCH 153/188] Switch to standard naming scheme Removes use of the special framework-modules naming scheme. Bug: 155164730 Test: m java Exempt-From-Owner-Approval: Build cleanup. Change-Id: I3b78fcbcacc3df787e171d6eedeef1e51b087615 Merged-In: I0c31e2183353dfb5bd49f04f3455cb7b10be6866 (cherry picked from 8b864fb45ce79051437f13c2a19510718ea3b7aa) --- Tethering/Android.bp | 2 +- Tethering/common/TetheringLib/Android.bp | 5 ----- Tethering/tests/unit/Android.bp | 2 +- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/Tethering/Android.bp b/Tethering/Android.bp index 8ae30a5c6f..d07a70c1af 100644 --- a/Tethering/Android.bp +++ b/Tethering/Android.bp @@ -34,7 +34,7 @@ java_defaults { ], libs: [ "framework-tethering.impl", - "framework-wifi-stubs-systemapi", + "framework-wifi", "unsupportedappusage", ], plugins: ["java_api_finder"], diff --git a/Tethering/common/TetheringLib/Android.bp b/Tethering/common/TetheringLib/Android.bp index 6c05b11258..c8becce7be 100644 --- a/Tethering/common/TetheringLib/Android.bp +++ b/Tethering/common/TetheringLib/Android.bp @@ -30,11 +30,6 @@ java_sdk_library { ":framework-tethering-srcs", ], - // TODO(b/155480189) - Remove naming_scheme once references have been resolved. - // Temporary java_sdk_library component naming scheme to use to ease the transition from separate - // modules to java_sdk_library. - naming_scheme: "framework-modules", - jarjar_rules: "jarjar-rules.txt", installable: true, diff --git a/Tethering/tests/unit/Android.bp b/Tethering/tests/unit/Android.bp index fccc6902e3..45c7b656e2 100644 --- a/Tethering/tests/unit/Android.bp +++ b/Tethering/tests/unit/Android.bp @@ -60,7 +60,7 @@ java_defaults { "framework-minus-apex", "framework-res", "framework-tethering.impl", - "framework-wifi-stubs-module_libs_api", + "framework-wifi.stubs.module_lib", ], jni_libs: [ // For mockito extended From 601971d2d22f5eee8577c7888eefdb4b3c334ba7 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Wed, 10 Jun 2020 00:59:37 +0900 Subject: [PATCH 154/188] Use the latest stable netd AIDL interface in Tethering. This is required to use the new BPF tethering APIs that set data usage limits. AOSP and master are already using -unstable instead of -V3. In order not to change master, the Merged-In tag is set to the CL that switched AOSP (and master) to -unstable. Test: m Bug: 150736748 Merged-In: I2d2cedf560319653f67f6b06f7abb0bf66eba91a Change-Id: Idba29b0fe428ac6552a025dcbe15729739088e9e --- Tethering/Android.bp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tethering/Android.bp b/Tethering/Android.bp index d07a70c1af..32e2b04e8e 100644 --- a/Tethering/Android.bp +++ b/Tethering/Android.bp @@ -25,7 +25,7 @@ java_defaults { ], static_libs: [ "androidx.annotation_annotation", - "netd_aidl_interface-V3-java", + "netd_aidl_interface-java", "netlink-client", "networkstack-aidl-interfaces-java", "android.hardware.tetheroffload.config-V1.0-java", From 0fffc850524a05f9d29b1f6c75d35bb3f9e998d6 Mon Sep 17 00:00:00 2001 From: Mark Chien Date: Tue, 9 Jun 2020 09:24:40 +0000 Subject: [PATCH 155/188] Move TetheringTests to presubmit The TetheringTests are green in postsubmit for a while. Also add TetheringIntegrationTests to postsubmit. Bug: 148998835 Test: atest --test-mapping frameworks/base/packages/Tethering:postsubmit atest --test-mapping frameworks/base/packages/Tethering:presubmit Merged-In: I80d29d06fde5a718c31a0a5ad2e2562be5bf419f Change-Id: I80d29d06fde5a718c31a0a5ad2e2562be5bf419f --- Tethering/TEST_MAPPING | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Tethering/TEST_MAPPING b/Tethering/TEST_MAPPING index 73254cdc79..5617b0c13c 100644 --- a/Tethering/TEST_MAPPING +++ b/Tethering/TEST_MAPPING @@ -1,7 +1,12 @@ { - "postsubmit": [ + "presubmit": [ { "name": "TetheringTests" } + ], + "postsubmit": [ + { + "name": "TetheringIntegrationTests" + } ] } From 020cb554f51448d6b6b771644a3693b9e9275c6b Mon Sep 17 00:00:00 2001 From: Hungming Chen Date: Tue, 2 Jun 2020 00:13:20 +0000 Subject: [PATCH 156/188] [BOT.1] Add a class ForwardedStats in TetheringUtils Used to record offload transmitted/received forwarded bytes/packets. Bug: 150736748 Test: new test BpfTetheringCoordinatorTest Original-Change: https://android-review.googlesource.com/1306257 Merged-In: Ie8725f95c3ddd5fb3811d479de32d2c1f7dcb493 Change-Id: Ie8725f95c3ddd5fb3811d479de32d2c1f7dcb493 --- .../src/android/net/util/TetheringUtils.java | 77 ++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/Tethering/src/android/net/util/TetheringUtils.java b/Tethering/src/android/net/util/TetheringUtils.java index dd67dddae1..b17b4ba77c 100644 --- a/Tethering/src/android/net/util/TetheringUtils.java +++ b/Tethering/src/android/net/util/TetheringUtils.java @@ -15,18 +15,93 @@ */ package android.net.util; +import android.net.TetherStatsParcel; import android.net.TetheringRequestParcel; +import androidx.annotation.NonNull; + import java.io.FileDescriptor; import java.net.SocketException; import java.util.Objects; /** - * Native methods for tethering utilization. + * The classes and the methods for tethering utilization. * * {@hide} */ public class TetheringUtils { + /** + * The object which records offload Tx/Rx forwarded bytes/packets. + * TODO: Replace the inner class ForwardedStats of class OffloadHardwareInterface with + * this class as well. + */ + public static class ForwardedStats { + public final long rxBytes; + public final long rxPackets; + public final long txBytes; + public final long txPackets; + + public ForwardedStats() { + rxBytes = 0; + rxPackets = 0; + txBytes = 0; + txPackets = 0; + } + + public ForwardedStats(long rxBytes, long txBytes) { + this.rxBytes = rxBytes; + this.rxPackets = 0; + this.txBytes = txBytes; + this.txPackets = 0; + } + + public ForwardedStats(long rxBytes, long rxPackets, long txBytes, long txPackets) { + this.rxBytes = rxBytes; + this.rxPackets = rxPackets; + this.txBytes = txBytes; + this.txPackets = txPackets; + } + + public ForwardedStats(@NonNull TetherStatsParcel tetherStats) { + rxBytes = tetherStats.rxBytes; + rxPackets = tetherStats.rxPackets; + txBytes = tetherStats.txBytes; + txPackets = tetherStats.txPackets; + } + + public ForwardedStats(@NonNull ForwardedStats other) { + rxBytes = other.rxBytes; + rxPackets = other.rxPackets; + txBytes = other.txBytes; + txPackets = other.txPackets; + } + + /** Add Tx/Rx bytes/packets and return the result as a new object. */ + @NonNull + public ForwardedStats add(@NonNull ForwardedStats other) { + return new ForwardedStats(rxBytes + other.rxBytes, rxPackets + other.rxPackets, + txBytes + other.txBytes, txPackets + other.txPackets); + } + + /** Subtract Tx/Rx bytes/packets and return the result as a new object. */ + @NonNull + public ForwardedStats subtract(@NonNull ForwardedStats other) { + // TODO: Perhaps throw an exception if any negative difference value just in case. + final long rxBytesDiff = Math.max(rxBytes - other.rxBytes, 0); + final long rxPacketsDiff = Math.max(rxPackets - other.rxPackets, 0); + final long txBytesDiff = Math.max(txBytes - other.txBytes, 0); + final long txPacketsDiff = Math.max(txPackets - other.txPackets, 0); + return new ForwardedStats(rxBytesDiff, rxPacketsDiff, txBytesDiff, txPacketsDiff); + } + + /** Returns the string representation of this object. */ + @NonNull + public String toString() { + return String.format("ForwardedStats(rxb: %d, rxp: %d, txb: %d, txp: %d)", rxBytes, + rxPackets, txBytes, txPackets); + } + } + /** * Configures a socket for receiving ICMPv6 router solicitations and sending advertisements. * @param fd the socket's {@link FileDescriptor}. From d50f53b58a4be9cad2c2ef306b1c67dcd54c6fb3 Mon Sep 17 00:00:00 2001 From: Hungming Chen Date: Tue, 2 Jun 2020 00:13:26 +0000 Subject: [PATCH 157/188] [BOT.2] Create a coordinator and stats provider to provide tether stats Make BPF tethering offload coordinator, BpfCoordinator, registers a network stats provider, BpfTetherStatsProvider, and provide the tethering stats from the BPF map. Bug: 150736748 Test: new test BpfCoordinatorTest Original-Change: https://android-review.googlesource.com/1256189 Merged-In: I22e71f87b67668f7e733e4f215d93bf5b2c9380d Change-Id: I22e71f87b67668f7e733e4f215d93bf5b2c9380d --- Tethering/src/android/net/ip/IpServer.java | 15 +- .../tethering/BpfCoordinator.java | 280 ++++++++++++++++++ .../networkstack/tethering/Tethering.java | 9 +- .../tethering/TetheringDependencies.java | 11 + .../unit/src/android/net/ip/IpServerTest.java | 8 +- .../networkstack/tethering/TetheringTest.java | 7 + 6 files changed, 325 insertions(+), 5 deletions(-) create mode 100644 Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java index 3fd9ee9a33..5f182a9cc0 100644 --- a/Tethering/src/android/net/ip/IpServer.java +++ b/Tethering/src/android/net/ip/IpServer.java @@ -65,6 +65,7 @@ import androidx.annotation.Nullable; import com.android.internal.util.MessageUtils; import com.android.internal.util.State; import com.android.internal.util.StateMachine; +import com.android.networkstack.tethering.BpfCoordinator; import com.android.networkstack.tethering.PrivateAddressCoordinator; import java.io.IOException; @@ -225,6 +226,8 @@ public class IpServer extends StateMachine { private final SharedLog mLog; private final INetd mNetd; + @NonNull + private final BpfCoordinator mBpfCoordinator; private final Callback mCallback; private final InterfaceController mInterfaceCtrl; private final PrivateAddressCoordinator mPrivateAddressCoordinator; @@ -314,11 +317,13 @@ public class IpServer extends StateMachine { // object. It helps to reduce the arguments of the constructor. public IpServer( String ifaceName, Looper looper, int interfaceType, SharedLog log, - INetd netd, Callback callback, boolean usingLegacyDhcp, boolean usingBpfOffload, + INetd netd, @NonNull BpfCoordinator coordinator, Callback callback, + boolean usingLegacyDhcp, boolean usingBpfOffload, PrivateAddressCoordinator addressCoordinator, Dependencies deps) { super(ifaceName, looper); mLog = log.forSubComponent(ifaceName); mNetd = netd; + mBpfCoordinator = coordinator; mCallback = callback; mInterfaceCtrl = new InterfaceController(ifaceName, mNetd, mLog); mIfaceName = ifaceName; @@ -749,6 +754,14 @@ public class IpServer extends StateMachine { } upstreamIfindex = mDeps.getIfindex(upstreamIface); + + // Add upstream index to name mapping for the tether stats usage in the coordinator. + // Although this mapping could be added by both class Tethering and IpServer, adding + // mapping from IpServer guarantees that the mapping is added before the adding + // forwarding rules. That is because there are different state machines in both + // classes. It is hard to guarantee the link property update order between multiple + // state machines. + mBpfCoordinator.addUpstreamNameToLookupTable(upstreamIfindex, upstreamIface); } // If v6only is null, we pass in null to setRaParams(), which handles diff --git a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java new file mode 100644 index 0000000000..0092eb7ee3 --- /dev/null +++ b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.networkstack.tethering; + +import static android.net.NetworkStats.DEFAULT_NETWORK_NO; +import static android.net.NetworkStats.METERED_NO; +import static android.net.NetworkStats.ROAMING_NO; +import static android.net.NetworkStats.SET_DEFAULT; +import static android.net.NetworkStats.TAG_NONE; +import static android.net.NetworkStats.UID_ALL; +import static android.net.NetworkStats.UID_TETHERING; + +import android.app.usage.NetworkStatsManager; +import android.net.INetd; +import android.net.NetworkStats; +import android.net.NetworkStats.Entry; +import android.net.TetherStatsParcel; +import android.net.netstats.provider.NetworkStatsProvider; +import android.net.util.SharedLog; +import android.net.util.TetheringUtils.ForwardedStats; +import android.os.Handler; +import android.os.RemoteException; +import android.os.ServiceSpecificException; +import android.util.Log; +import android.util.SparseArray; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +/** + * This coordinator is responsible for providing BPF offload relevant functionality. + * - Get tethering stats. + * + * @hide + */ +public class BpfCoordinator { + private static final String TAG = BpfCoordinator.class.getSimpleName(); + // TODO: Make it customizable. + private static final int DEFAULT_PERFORM_POLL_INTERVAL_MS = 5000; + + private enum StatsType { + STATS_PER_IFACE, + STATS_PER_UID, + } + + @NonNull + private final Handler mHandler; + @NonNull + private final INetd mNetd; + @NonNull + private final SharedLog mLog; + @NonNull + private final Dependencies mDeps; + @Nullable + private final BpfTetherStatsProvider mStatsProvider; + private boolean mStarted = false; + + // Maps upstream interface index to offloaded traffic statistics. + // Always contains the latest total bytes/packets, since each upstream was started, received + // from the BPF maps for each interface. + private SparseArray mStats = new SparseArray<>(); + + // Maps upstream interface index to interface names. + // Store all interface name since boot. Used for lookup what interface name it is from the + // tether stats got from netd because netd reports interface index to present an interface. + // TODO: Remove the unused interface name. + private SparseArray mInterfaceNames = new SparseArray<>(); + + // Runnable that used by scheduling next polling of stats. + private final Runnable mScheduledPollingTask = () -> { + updateForwardedStatsFromNetd(); + maybeSchedulePollingStats(); + }; + + static class Dependencies { + int getPerformPollInterval() { + // TODO: Consider make this configurable. + return DEFAULT_PERFORM_POLL_INTERVAL_MS; + } + } + + BpfCoordinator(@NonNull Handler handler, @NonNull INetd netd, + @NonNull NetworkStatsManager nsm, @NonNull SharedLog log, @NonNull Dependencies deps) { + mHandler = handler; + mNetd = netd; + mLog = log.forSubComponent(TAG); + BpfTetherStatsProvider provider = new BpfTetherStatsProvider(); + try { + nsm.registerNetworkStatsProvider(getClass().getSimpleName(), provider); + } catch (RuntimeException e) { + // TODO: Perhaps not allow to use BPF offload because the reregistration failure + // implied that no data limit could be applies on a metered upstream if any. + Log.wtf(TAG, "Cannot register offload stats provider: " + e); + provider = null; + } + mStatsProvider = provider; + mDeps = deps; + } + + /** + * Start BPF tethering offload stats polling when the first upstream is started. + * Note that this can be only called on handler thread. + * TODO: Perhaps check BPF support before starting. + * TODO: Start the stats polling only if there is any client on the downstream. + */ + public void start() { + if (mStarted) return; + + mStarted = true; + maybeSchedulePollingStats(); + + mLog.i("BPF tethering coordinator started"); + } + + /** + * Stop BPF tethering offload stats polling and cleanup upstream parameters. + * Note that this can be only called on handler thread. + */ + public void stop() { + if (!mStarted) return; + + // Stop scheduled polling tasks and poll the latest stats from BPF maps. + if (mHandler.hasCallbacks(mScheduledPollingTask)) { + mHandler.removeCallbacks(mScheduledPollingTask); + } + updateForwardedStatsFromNetd(); + + mStarted = false; + + mLog.i("BPF tethering coordinator stopped"); + } + + /** + * Add upstream name to lookup table. The lookup table is used for tether stats interface name + * lookup because the netd only reports interface index in BPF tether stats but the service + * expects the interface name in NetworkStats object. + * Note that this can be only called on handler thread. + */ + public void addUpstreamNameToLookupTable(int upstreamIfindex, String upstreamIface) { + if (upstreamIfindex == 0) return; + + // The same interface index to name mapping may be added by different IpServer objects or + // re-added by reconnection on the same upstream interface. Ignore the duplicate one. + final String iface = mInterfaceNames.get(upstreamIfindex); + if (iface == null) { + mInterfaceNames.put(upstreamIfindex, upstreamIface); + } else if (iface != upstreamIface) { + Log.wtf(TAG, "The upstream interface name " + upstreamIface + + " is different from the existing interface name " + + iface + " for index " + upstreamIfindex); + } + } + + /** + * A BPF tethering stats provider to provide network statistics to the system. + * Note that this class's data may only be accessed on the handler thread. + */ + private class BpfTetherStatsProvider extends NetworkStatsProvider { + // The offloaded traffic statistics per interface that has not been reported since the + // last call to pushTetherStats. Only the interfaces that were ever tethering upstreams + // and has pending tether stats delta are included in this NetworkStats object. + private NetworkStats mIfaceStats = new NetworkStats(0L, 0); + + // The same stats as above, but counts network stats per uid. + private NetworkStats mUidStats = new NetworkStats(0L, 0); + + @Override + public void onRequestStatsUpdate(int token) { + mHandler.post(() -> pushTetherStats()); + } + + @Override + public void onSetAlert(long quotaBytes) { + // no-op + } + + @Override + public void onSetLimit(@NonNull String iface, long quotaBytes) { + // no-op + } + + private void pushTetherStats() { + try { + // The token is not used for now. See b/153606961. + notifyStatsUpdated(0 /* token */, mIfaceStats, mUidStats); + + // Clear the accumulated tether stats delta after reported. Note that create a new + // empty object because NetworkStats#clear is @hide. + mIfaceStats = new NetworkStats(0L, 0); + mUidStats = new NetworkStats(0L, 0); + } catch (RuntimeException e) { + mLog.e("Cannot report network stats: ", e); + } + } + + private void accumulateDiff(@NonNull NetworkStats ifaceDiff, + @NonNull NetworkStats uidDiff) { + mIfaceStats = mIfaceStats.add(ifaceDiff); + mUidStats = mUidStats.add(uidDiff); + } + } + + @NonNull + private NetworkStats buildNetworkStats(@NonNull StatsType type, int ifIndex, + @NonNull ForwardedStats diff) { + NetworkStats stats = new NetworkStats(0L, 0); + final String iface = mInterfaceNames.get(ifIndex); + if (iface == null) { + // TODO: Use Log.wtf once the coordinator owns full control of tether stats from netd. + // For now, netd may add the empty stats for the upstream which is not monitored by + // the coordinator. Silently ignore it. + return stats; + } + final int uid = (type == StatsType.STATS_PER_UID) ? UID_TETHERING : UID_ALL; + // Note that the argument 'metered', 'roaming' and 'defaultNetwork' are not recorded for + // network stats snapshot. See NetworkStatsRecorder#recordSnapshotLocked. + return stats.addEntry(new Entry(iface, uid, SET_DEFAULT, TAG_NONE, METERED_NO, + ROAMING_NO, DEFAULT_NETWORK_NO, diff.rxBytes, diff.rxPackets, + diff.txBytes, diff.txPackets, 0L /* operations */)); + } + + private void updateForwardedStatsFromNetd() { + final TetherStatsParcel[] tetherStatsList; + try { + // The reported tether stats are total data usage for all currently-active upstream + // interfaces since tethering start. + tetherStatsList = mNetd.tetherOffloadGetStats(); + } catch (RemoteException | ServiceSpecificException e) { + mLog.e("Problem fetching tethering stats: ", e); + return; + } + + for (TetherStatsParcel tetherStats : tetherStatsList) { + final Integer ifIndex = tetherStats.ifIndex; + final ForwardedStats curr = new ForwardedStats(tetherStats); + final ForwardedStats base = mStats.get(ifIndex); + final ForwardedStats diff = (base != null) ? curr.subtract(base) : curr; + + // Update the local cache for counting tether stats delta. + mStats.put(ifIndex, curr); + + // Update the accumulated tether stats delta to the stats provider for the service + // querying. + if (mStatsProvider != null) { + try { + mStatsProvider.accumulateDiff( + buildNetworkStats(StatsType.STATS_PER_IFACE, ifIndex, diff), + buildNetworkStats(StatsType.STATS_PER_UID, ifIndex, diff)); + } catch (ArrayIndexOutOfBoundsException e) { + Log.wtf("Fail to update the accumulated stats delta for interface index " + + ifIndex + " : ", e); + } + } + } + } + + private void maybeSchedulePollingStats() { + if (!mStarted) return; + + if (mHandler.hasCallbacks(mScheduledPollingTask)) { + mHandler.removeCallbacks(mScheduledPollingTask); + } + + mHandler.postDelayed(mScheduledPollingTask, mDeps.getPerformPollInterval()); + } +} diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java index 69eec8df98..cfe9feade8 100644 --- a/Tethering/src/com/android/networkstack/tethering/Tethering.java +++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java @@ -232,6 +232,7 @@ public class Tethering { private final TetheringThreadExecutor mExecutor; private final TetheringNotificationUpdater mNotificationUpdater; private final UserManager mUserManager; + private final BpfCoordinator mBpfCoordinator; private final PrivateAddressCoordinator mPrivateAddressCoordinator; private int mActiveDataSubId = INVALID_SUBSCRIPTION_ID; // All the usage of mTetheringEventCallback should run in the same thread. @@ -284,6 +285,8 @@ public class Tethering { mUpstreamNetworkMonitor = mDeps.getUpstreamNetworkMonitor(mContext, mTetherMasterSM, mLog, TetherMasterSM.EVENT_UPSTREAM_CALLBACK); mForwardedDownstreams = new LinkedHashSet<>(); + mBpfCoordinator = mDeps.getBpfCoordinator( + mHandler, mNetd, mLog, new BpfCoordinator.Dependencies()); IntentFilter filter = new IntentFilter(); filter.addAction(ACTION_CARRIER_CONFIG_CHANGED); @@ -1704,6 +1707,9 @@ public class Tethering { chooseUpstreamType(true); mTryCell = false; } + + // TODO: Check the upstream interface if it is managed by BPF offload. + mBpfCoordinator.start(); } @Override @@ -1716,6 +1722,7 @@ public class Tethering { mTetherUpstream = null; reportUpstreamChanged(null); } + mBpfCoordinator.stop(); } private boolean updateUpstreamWanted() { @@ -2341,7 +2348,7 @@ public class Tethering { mLog.log("adding TetheringInterfaceStateMachine for: " + iface); final TetherState tetherState = new TetherState( - new IpServer(iface, mLooper, interfaceType, mLog, mNetd, + new IpServer(iface, mLooper, interfaceType, mLog, mNetd, mBpfCoordinator, makeControlCallback(), mConfig.enableLegacyDhcpServer, mConfig.enableBpfOffload, mPrivateAddressCoordinator, mDeps.getIpServerDependencies())); diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java b/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java index ce546c701a..d637c8646b 100644 --- a/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java @@ -40,6 +40,17 @@ import java.util.ArrayList; * @hide */ public abstract class TetheringDependencies { + /** + * Get a reference to the BpfCoordinator to be used by tethering. + */ + public @NonNull BpfCoordinator getBpfCoordinator( + @NonNull Handler handler, @NonNull INetd netd, @NonNull SharedLog log, + @NonNull BpfCoordinator.Dependencies deps) { + final NetworkStatsManager statsManager = + (NetworkStatsManager) getContext().getSystemService(Context.NETWORK_STATS_SERVICE); + return new BpfCoordinator(handler, netd, statsManager, log, deps); + } + /** * Get a reference to the offload hardware interface to be used by tethering. */ diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java index 0cda29a32f..433aacfaff 100644 --- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java +++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java @@ -87,6 +87,7 @@ import android.text.TextUtils; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.networkstack.tethering.BpfCoordinator; import com.android.networkstack.tethering.PrivateAddressCoordinator; import org.junit.Before; @@ -126,6 +127,7 @@ public class IpServerTest { private final IpPrefix mBluetoothPrefix = new IpPrefix("192.168.44.0/24"); @Mock private INetd mNetd; + @Mock private BpfCoordinator mBpfCoordinator; @Mock private IpServer.Callback mCallback; @Mock private SharedLog mSharedLog; @Mock private IDhcpServer mDhcpServer; @@ -179,7 +181,7 @@ public class IpServerTest { neighborCaptor.capture()); mIpServer = new IpServer( - IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd, + IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd, mBpfCoordinator, mCallback, usingLegacyDhcp, usingBpfOffload, mAddressCoordinator, mDependencies); mIpServer.start(); mNeighborEventConsumer = neighborCaptor.getValue(); @@ -222,8 +224,8 @@ public class IpServerTest { when(mDependencies.getIpNeighborMonitor(any(), any(), any())) .thenReturn(mIpNeighborMonitor); mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), TETHERING_BLUETOOTH, mSharedLog, - mNetd, mCallback, false /* usingLegacyDhcp */, DEFAULT_USING_BPF_OFFLOAD, - mAddressCoordinator, mDependencies); + mNetd, mBpfCoordinator, mCallback, false /* usingLegacyDhcp */, + DEFAULT_USING_BPF_OFFLOAD, mAddressCoordinator, mDependencies); mIpServer.start(); mLooper.dispatchAll(); verify(mCallback).updateInterfaceState( 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 bb65b18edb..8146a58ddd 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java @@ -203,6 +203,7 @@ public class TetheringTest { @Mock private ConnectivityManager mCm; @Mock private EthernetManager mEm; @Mock private TetheringNotificationUpdater mNotificationUpdater; + @Mock private BpfCoordinator mBpfCoordinator; private final MockIpServerDependencies mIpServerDependencies = spy(new MockIpServerDependencies()); @@ -336,6 +337,12 @@ public class TetheringTest { mIpv6CoordinatorNotifyList = null; } + @Override + public BpfCoordinator getBpfCoordinator(Handler handler, INetd netd, + SharedLog log, BpfCoordinator.Dependencies deps) { + return mBpfCoordinator; + } + @Override public OffloadHardwareInterface getOffloadHardwareInterface(Handler h, SharedLog log) { return mOffloadHardwareInterface; From 6442858f9d8cb8582f788f02d4845f8178ec4f3f Mon Sep 17 00:00:00 2001 From: Hungming Chen Date: Tue, 2 Jun 2020 00:13:40 +0000 Subject: [PATCH 158/188] [BOT.3] Add unit test for polling network stats in the coordinator Verify that the coordinator could fetch tether stats from BPF maps and report the network stats to the service. Bug: 150736748 Test: atest BpfCoordinatorTest Original-Change: https://android-review.googlesource.com/1305574 Merged-In: Ib1756159a2047c5db7d31359b0f288f840bd1bb1 Change-Id: Ib1756159a2047c5db7d31359b0f288f840bd1bb1 --- .../tethering/BpfCoordinator.java | 16 +- .../tethering/BpfCoordinatorTest.java | 207 ++++++++++++++++++ 2 files changed, 218 insertions(+), 5 deletions(-) create mode 100644 Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java diff --git a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java index 0092eb7ee3..aded6cf73c 100644 --- a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java +++ b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java @@ -41,6 +41,8 @@ import android.util.SparseArray; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.internal.annotations.VisibleForTesting; + /** * This coordinator is responsible for providing BPF offload relevant functionality. * - Get tethering stats. @@ -49,10 +51,11 @@ import androidx.annotation.Nullable; */ public class BpfCoordinator { private static final String TAG = BpfCoordinator.class.getSimpleName(); - // TODO: Make it customizable. - private static final int DEFAULT_PERFORM_POLL_INTERVAL_MS = 5000; + @VisibleForTesting + static final int DEFAULT_PERFORM_POLL_INTERVAL_MS = 5000; // TODO: Make it customizable. - private enum StatsType { + @VisibleForTesting + enum StatsType { STATS_PER_IFACE, STATS_PER_UID, } @@ -86,6 +89,7 @@ public class BpfCoordinator { maybeSchedulePollingStats(); }; + @VisibleForTesting static class Dependencies { int getPerformPollInterval() { // TODO: Consider make this configurable. @@ -169,7 +173,8 @@ public class BpfCoordinator { * A BPF tethering stats provider to provide network statistics to the system. * Note that this class's data may only be accessed on the handler thread. */ - private class BpfTetherStatsProvider extends NetworkStatsProvider { + @VisibleForTesting + class BpfTetherStatsProvider extends NetworkStatsProvider { // The offloaded traffic statistics per interface that has not been reported since the // last call to pushTetherStats. Only the interfaces that were ever tethering upstreams // and has pending tether stats delta are included in this NetworkStats object. @@ -193,7 +198,8 @@ public class BpfCoordinator { // no-op } - private void pushTetherStats() { + @VisibleForTesting + void pushTetherStats() { try { // The token is not used for now. See b/153606961. notifyStatsUpdated(0 /* token */, mIfaceStats, mUidStats); diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java new file mode 100644 index 0000000000..b029b43d19 --- /dev/null +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.networkstack.tethering; + +import static android.net.NetworkStats.DEFAULT_NETWORK_NO; +import static android.net.NetworkStats.METERED_NO; +import static android.net.NetworkStats.ROAMING_NO; +import static android.net.NetworkStats.SET_DEFAULT; +import static android.net.NetworkStats.TAG_NONE; +import static android.net.NetworkStats.UID_ALL; +import static android.net.NetworkStats.UID_TETHERING; + +import static com.android.networkstack.tethering.BpfCoordinator + .DEFAULT_PERFORM_POLL_INTERVAL_MS; +import static com.android.networkstack.tethering.BpfCoordinator.StatsType; +import static com.android.networkstack.tethering.BpfCoordinator.StatsType.STATS_PER_IFACE; +import static com.android.networkstack.tethering.BpfCoordinator.StatsType.STATS_PER_UID; + +import static junit.framework.Assert.assertNotNull; + +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.annotation.NonNull; +import android.app.usage.NetworkStatsManager; +import android.net.INetd; +import android.net.NetworkStats; +import android.net.TetherStatsParcel; +import android.net.util.SharedLog; +import android.os.Handler; +import android.os.test.TestLooper; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.testutils.TestableNetworkStatsProviderCbBinder; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class BpfCoordinatorTest { + @Mock private NetworkStatsManager mStatsManager; + @Mock private INetd mNetd; + // Late init since methods must be called by the thread that created this object. + private TestableNetworkStatsProviderCbBinder mTetherStatsProviderCb; + private BpfCoordinator.BpfTetherStatsProvider mTetherStatsProvider; + private final ArgumentCaptor mStringArrayCaptor = + ArgumentCaptor.forClass(ArrayList.class); + private final TestLooper mTestLooper = new TestLooper(); + private BpfCoordinator.Dependencies mDeps = + new BpfCoordinator.Dependencies() { + @Override + int getPerformPollInterval() { + return DEFAULT_PERFORM_POLL_INTERVAL_MS; + } + }; + + @Before public void setUp() { + MockitoAnnotations.initMocks(this); + } + + private void waitForIdle() { + mTestLooper.dispatchAll(); + } + + private void setupFunctioningNetdInterface() throws Exception { + when(mNetd.tetherOffloadGetStats()).thenReturn(new TetherStatsParcel[0]); + } + + @NonNull + private BpfCoordinator makeBpfCoordinator() throws Exception { + BpfCoordinator coordinator = new BpfCoordinator( + new Handler(mTestLooper.getLooper()), mNetd, mStatsManager, new SharedLog("test"), + mDeps); + final ArgumentCaptor + tetherStatsProviderCaptor = + ArgumentCaptor.forClass(BpfCoordinator.BpfTetherStatsProvider.class); + verify(mStatsManager).registerNetworkStatsProvider(anyString(), + tetherStatsProviderCaptor.capture()); + mTetherStatsProvider = tetherStatsProviderCaptor.getValue(); + assertNotNull(mTetherStatsProvider); + mTetherStatsProviderCb = new TestableNetworkStatsProviderCbBinder(); + mTetherStatsProvider.setProviderCallbackBinder(mTetherStatsProviderCb); + return coordinator; + } + + @NonNull + private static NetworkStats.Entry buildTestEntry(@NonNull StatsType how, + @NonNull String iface, long rxBytes, long rxPackets, long txBytes, long txPackets) { + return new NetworkStats.Entry(iface, how == STATS_PER_IFACE ? UID_ALL : UID_TETHERING, + SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, rxBytes, + rxPackets, txBytes, txPackets, 0L); + } + + @NonNull + private static TetherStatsParcel buildTestTetherStatsParcel(@NonNull Integer ifIndex, + long rxBytes, long rxPackets, long txBytes, long txPackets) { + final TetherStatsParcel parcel = new TetherStatsParcel(); + parcel.ifIndex = ifIndex; + parcel.rxBytes = rxBytes; + parcel.rxPackets = rxPackets; + parcel.txBytes = txBytes; + parcel.txPackets = txPackets; + return parcel; + } + + private void setTetherOffloadStatsList(TetherStatsParcel[] tetherStatsList) throws Exception { + when(mNetd.tetherOffloadGetStats()).thenReturn(tetherStatsList); + mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS); + waitForIdle(); + } + + @Test + public void testGetForwardedStats() throws Exception { + setupFunctioningNetdInterface(); + + final BpfCoordinator coordinator = makeBpfCoordinator(); + coordinator.start(); + + final String wlanIface = "wlan0"; + final Integer wlanIfIndex = 100; + final String mobileIface = "rmnet_data0"; + final Integer mobileIfIndex = 101; + + // Add interface name to lookup table. In realistic case, the upstream interface name will + // be added by IpServer when IpServer has received with a new IPv6 upstream update event. + coordinator.addUpstreamNameToLookupTable(wlanIfIndex, wlanIface); + coordinator.addUpstreamNameToLookupTable(mobileIfIndex, mobileIface); + + // [1] Both interface stats are changed. + // Setup the tether stats of wlan and mobile interface. Note that move forward the time of + // the looper to make sure the new tether stats has been updated by polling update thread. + setTetherOffloadStatsList(new TetherStatsParcel[] { + buildTestTetherStatsParcel(wlanIfIndex, 1000, 100, 2000, 200), + buildTestTetherStatsParcel(mobileIfIndex, 3000, 300, 4000, 400)}); + + final NetworkStats expectedIfaceStats = new NetworkStats(0L, 2) + .addEntry(buildTestEntry(STATS_PER_IFACE, wlanIface, 1000, 100, 2000, 200)) + .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 3000, 300, 4000, 400)); + + final NetworkStats expectedUidStats = new NetworkStats(0L, 2) + .addEntry(buildTestEntry(STATS_PER_UID, wlanIface, 1000, 100, 2000, 200)) + .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 3000, 300, 4000, 400)); + + // Force pushing stats update to verify the stats reported. + // TODO: Perhaps make #expectNotifyStatsUpdated to use test TetherStatsParcel object for + // verifying the notification. + mTetherStatsProvider.pushTetherStats(); + mTetherStatsProviderCb.expectNotifyStatsUpdated(expectedIfaceStats, expectedUidStats); + + // [2] Only one interface stats is changed. + // The tether stats of mobile interface is accumulated and The tether stats of wlan + // interface is the same. + setTetherOffloadStatsList(new TetherStatsParcel[] { + buildTestTetherStatsParcel(wlanIfIndex, 1000, 100, 2000, 200), + buildTestTetherStatsParcel(mobileIfIndex, 3010, 320, 4030, 440)}); + + final NetworkStats expectedIfaceStatsDiff = new NetworkStats(0L, 2) + .addEntry(buildTestEntry(STATS_PER_IFACE, wlanIface, 0, 0, 0, 0)) + .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 10, 20, 30, 40)); + + final NetworkStats expectedUidStatsDiff = new NetworkStats(0L, 2) + .addEntry(buildTestEntry(STATS_PER_UID, wlanIface, 0, 0, 0, 0)) + .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 10, 20, 30, 40)); + + // Force pushing stats update to verify that only diff of stats is reported. + mTetherStatsProvider.pushTetherStats(); + mTetherStatsProviderCb.expectNotifyStatsUpdated(expectedIfaceStatsDiff, + expectedUidStatsDiff); + + // [3] Stop coordinator. + // Shutdown the coordinator and clear the invocation history, especially the + // tetherOffloadGetStats() calls. + coordinator.stop(); + clearInvocations(mNetd); + + // Verify the polling update thread stopped. + mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS); + waitForIdle(); + verify(mNetd, never()).tetherOffloadGetStats(); + } +} From 810a381cd47f8015f61d772f027c5fcccc95ff2b Mon Sep 17 00:00:00 2001 From: Hungming Chen Date: Tue, 2 Jun 2020 00:13:45 +0000 Subject: [PATCH 159/188] [BOT.5] Move class Ipv6ForwardingRule from IpServer to the coordinator This is a preparation for moving adding/removing forwarding rules from IpServer to BpfCoordinator. Bug: 150736748 Test: atest IpServerTest Original-Change: https://android-review.googlesource.com/1317234 Merged-In: I85316ef09ff3c9389ded11dcc384493d699da48e Change-Id: I85316ef09ff3c9389ded11dcc384493d699da48e --- Tethering/src/android/net/ip/IpServer.java | 36 +--------------- .../tethering/BpfCoordinator.java | 43 +++++++++++++++++++ 2 files changed, 44 insertions(+), 35 deletions(-) diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java index 5f182a9cc0..830f6a0fec 100644 --- a/Tethering/src/android/net/ip/IpServer.java +++ b/Tethering/src/android/net/ip/IpServer.java @@ -33,7 +33,6 @@ import android.net.LinkAddress; import android.net.LinkProperties; import android.net.MacAddress; import android.net.RouteInfo; -import android.net.TetherOffloadRuleParcel; import android.net.TetheredClient; import android.net.TetheringManager; import android.net.TetheringRequestParcel; @@ -66,6 +65,7 @@ import com.android.internal.util.MessageUtils; import com.android.internal.util.State; import com.android.internal.util.StateMachine; import com.android.networkstack.tethering.BpfCoordinator; +import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule; import com.android.networkstack.tethering.PrivateAddressCoordinator; import java.io.IOException; @@ -272,40 +272,6 @@ public class IpServer extends StateMachine { } } - static class Ipv6ForwardingRule { - public final int upstreamIfindex; - public final int downstreamIfindex; - public final Inet6Address address; - public final MacAddress srcMac; - public final MacAddress dstMac; - - Ipv6ForwardingRule(int upstreamIfindex, int downstreamIfIndex, Inet6Address address, - MacAddress srcMac, MacAddress dstMac) { - this.upstreamIfindex = upstreamIfindex; - this.downstreamIfindex = downstreamIfIndex; - this.address = address; - this.srcMac = srcMac; - this.dstMac = dstMac; - } - - public Ipv6ForwardingRule onNewUpstream(int newUpstreamIfindex) { - return new Ipv6ForwardingRule(newUpstreamIfindex, downstreamIfindex, address, srcMac, - dstMac); - } - - // Don't manipulate TetherOffloadRuleParcel directly because implementing onNewUpstream() - // would be error-prone due to generated stable AIDL classes not having a copy constructor. - public TetherOffloadRuleParcel toTetherOffloadRuleParcel() { - final TetherOffloadRuleParcel parcel = new TetherOffloadRuleParcel(); - parcel.inputInterfaceIndex = upstreamIfindex; - parcel.outputInterfaceIndex = downstreamIfindex; - parcel.destination = address.getAddress(); - parcel.prefixLength = 128; - parcel.srcL2Address = srcMac.toByteArray(); - parcel.dstL2Address = dstMac.toByteArray(); - return parcel; - } - } private final LinkedHashMap mIpv6ForwardingRules = new LinkedHashMap<>(); diff --git a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java index aded6cf73c..6b854f2ac9 100644 --- a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java +++ b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java @@ -26,8 +26,10 @@ import static android.net.NetworkStats.UID_TETHERING; import android.app.usage.NetworkStatsManager; import android.net.INetd; +import android.net.MacAddress; import android.net.NetworkStats; import android.net.NetworkStats.Entry; +import android.net.TetherOffloadRuleParcel; import android.net.TetherStatsParcel; import android.net.netstats.provider.NetworkStatsProvider; import android.net.util.SharedLog; @@ -43,6 +45,8 @@ import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; +import java.net.Inet6Address; + /** * This coordinator is responsible for providing BPF offload relevant functionality. * - Get tethering stats. @@ -169,6 +173,45 @@ public class BpfCoordinator { } } + /** IPv6 forwarding rule class. */ + public static class Ipv6ForwardingRule { + public final int upstreamIfindex; + public final int downstreamIfindex; + public final Inet6Address address; + public final MacAddress srcMac; + public final MacAddress dstMac; + + public Ipv6ForwardingRule(int upstreamIfindex, int downstreamIfIndex, Inet6Address address, + MacAddress srcMac, MacAddress dstMac) { + this.upstreamIfindex = upstreamIfindex; + this.downstreamIfindex = downstreamIfIndex; + this.address = address; + this.srcMac = srcMac; + this.dstMac = dstMac; + } + + /** Return a new rule object which updates with new upstream index. */ + public Ipv6ForwardingRule onNewUpstream(int newUpstreamIfindex) { + return new Ipv6ForwardingRule(newUpstreamIfindex, downstreamIfindex, address, srcMac, + dstMac); + } + + /** + * Don't manipulate TetherOffloadRuleParcel directly because implementing onNewUpstream() + * would be error-prone due to generated stable AIDL classes not having a copy constructor. + */ + public TetherOffloadRuleParcel toTetherOffloadRuleParcel() { + final TetherOffloadRuleParcel parcel = new TetherOffloadRuleParcel(); + parcel.inputInterfaceIndex = upstreamIfindex; + parcel.outputInterfaceIndex = downstreamIfindex; + parcel.destination = address.getAddress(); + parcel.prefixLength = 128; + parcel.srcL2Address = srcMac.toByteArray(); + parcel.dstL2Address = dstMac.toByteArray(); + return parcel; + } + } + /** * A BPF tethering stats provider to provide network statistics to the system. * Note that this class's data may only be accessed on the handler thread. From 11b88351c0818b5cb09c8f3c23f5b3af946fc940 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Tue, 9 Jun 2020 02:55:01 +0000 Subject: [PATCH 160/188] [BOT.4] Make BpfCoordinator to support data warning Invoke the existing polling thread to update data alert statistics. Once the data alert limit has reached, trigger the notification. Bug: 150736748 Test: BpfCoordinatorTest Original-Change: https://android-review.googlesource.com/1302435 Merged-In: Ibf25560ca2e9f003d8eba01361dc7d35ec1b1627 Change-Id: Ibf25560ca2e9f003d8eba01361dc7d35ec1b1627 --- .../tethering/BpfCoordinator.java | 39 ++++++++++++++++--- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java index 6b854f2ac9..089b12aa39 100644 --- a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java +++ b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java @@ -23,6 +23,7 @@ import static android.net.NetworkStats.SET_DEFAULT; import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.UID_ALL; import static android.net.NetworkStats.UID_TETHERING; +import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED; import android.app.usage.NetworkStatsManager; import android.net.INetd; @@ -37,6 +38,7 @@ import android.net.util.TetheringUtils.ForwardedStats; import android.os.Handler; import android.os.RemoteException; import android.os.ServiceSpecificException; +import android.text.TextUtils; import android.util.Log; import android.util.SparseArray; @@ -50,6 +52,7 @@ import java.net.Inet6Address; /** * This coordinator is responsible for providing BPF offload relevant functionality. * - Get tethering stats. + * - Set global alert. * * @hide */ @@ -76,6 +79,10 @@ public class BpfCoordinator { private final BpfTetherStatsProvider mStatsProvider; private boolean mStarted = false; + // Tracking remaining alert quota. Unlike limit quota is subject to interface, the alert + // quota is interface independent and global for tether offload. + private long mRemainingAlertQuota = QUOTA_UNLIMITED; + // Maps upstream interface index to offloaded traffic statistics. // Always contains the latest total bytes/packets, since each upstream was started, received // from the BPF maps for each interface. @@ -158,15 +165,15 @@ public class BpfCoordinator { * expects the interface name in NetworkStats object. * Note that this can be only called on handler thread. */ - public void addUpstreamNameToLookupTable(int upstreamIfindex, String upstreamIface) { - if (upstreamIfindex == 0) return; + public void addUpstreamNameToLookupTable(int upstreamIfindex, @NonNull String upstreamIface) { + if (upstreamIfindex == 0 || TextUtils.isEmpty(upstreamIface)) return; // The same interface index to name mapping may be added by different IpServer objects or // re-added by reconnection on the same upstream interface. Ignore the duplicate one. final String iface = mInterfaceNames.get(upstreamIfindex); if (iface == null) { mInterfaceNames.put(upstreamIfindex, upstreamIface); - } else if (iface != upstreamIface) { + } else if (!TextUtils.equals(iface, upstreamIface)) { Log.wtf(TAG, "The upstream interface name " + upstreamIface + " is different from the existing interface name " + iface + " for index " + upstreamIfindex); @@ -214,7 +221,7 @@ public class BpfCoordinator { /** * A BPF tethering stats provider to provide network statistics to the system. - * Note that this class's data may only be accessed on the handler thread. + * Note that this class' data may only be accessed on the handler thread. */ @VisibleForTesting class BpfTetherStatsProvider extends NetworkStatsProvider { @@ -233,7 +240,7 @@ public class BpfCoordinator { @Override public void onSetAlert(long quotaBytes) { - // no-op + mHandler.post(() -> updateAlertQuota(quotaBytes)); } @Override @@ -282,6 +289,19 @@ public class BpfCoordinator { diff.txBytes, diff.txPackets, 0L /* operations */)); } + private void updateAlertQuota(long newQuota) { + if (newQuota < QUOTA_UNLIMITED) { + throw new IllegalArgumentException("invalid quota value " + newQuota); + } + if (mRemainingAlertQuota == newQuota) return; + + mRemainingAlertQuota = newQuota; + if (mRemainingAlertQuota == 0) { + mLog.i("onAlertReached"); + if (mStatsProvider != null) mStatsProvider.notifyAlertReached(); + } + } + private void updateForwardedStatsFromNetd() { final TetherStatsParcel[] tetherStatsList; try { @@ -293,11 +313,13 @@ public class BpfCoordinator { return; } + long usedAlertQuota = 0; for (TetherStatsParcel tetherStats : tetherStatsList) { final Integer ifIndex = tetherStats.ifIndex; final ForwardedStats curr = new ForwardedStats(tetherStats); final ForwardedStats base = mStats.get(ifIndex); final ForwardedStats diff = (base != null) ? curr.subtract(base) : curr; + usedAlertQuota += diff.rxBytes + diff.txBytes; // Update the local cache for counting tether stats delta. mStats.put(ifIndex, curr); @@ -315,6 +337,13 @@ public class BpfCoordinator { } } } + + if (mRemainingAlertQuota > 0 && usedAlertQuota > 0) { + // Trim to zero if overshoot. + final long newQuota = Math.max(mRemainingAlertQuota - usedAlertQuota, 0); + updateAlertQuota(newQuota); + } + } private void maybeSchedulePollingStats() { From 1aa15dab62f2c070d949006e550495abc9d8d285 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Tue, 9 Jun 2020 02:55:52 +0000 Subject: [PATCH 161/188] [BOT.9] Add unit test for data warning in BpfCoordinator Bug: 150736748 Test: atest BpfCoordinatorTest Original-Change: https://android-review.googlesource.com/1311658 Merged-In: Ic1f37de75b064d7c8717e1b496e13174bb8693ec Change-Id: Ic1f37de75b064d7c8717e1b496e13174bb8693ec --- .../tethering/BpfCoordinatorTest.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java index b029b43d19..3e19ddfc0b 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java @@ -23,6 +23,7 @@ import static android.net.NetworkStats.SET_DEFAULT; import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.UID_ALL; import static android.net.NetworkStats.UID_TETHERING; +import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED; import static com.android.networkstack.tethering.BpfCoordinator .DEFAULT_PERFORM_POLL_INTERVAL_MS; @@ -204,4 +205,42 @@ public class BpfCoordinatorTest { waitForIdle(); verify(mNetd, never()).tetherOffloadGetStats(); } + + @Test + public void testOnSetAlert() throws Exception { + setupFunctioningNetdInterface(); + + final BpfCoordinator coordinator = makeBpfCoordinator(); + coordinator.start(); + + final String mobileIface = "rmnet_data0"; + final Integer mobileIfIndex = 100; + coordinator.addUpstreamNameToLookupTable(mobileIfIndex, mobileIface); + + // Verify that set quota to 0 will immediately triggers a callback. + mTetherStatsProvider.onSetAlert(0); + waitForIdle(); + mTetherStatsProviderCb.expectNotifyAlertReached(); + + // Verify that notifyAlertReached never fired if quota is not yet reached. + when(mNetd.tetherOffloadGetStats()).thenReturn( + new TetherStatsParcel[] {buildTestTetherStatsParcel(mobileIfIndex, 0, 0, 0, 0)}); + mTetherStatsProvider.onSetAlert(100); + mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS); + waitForIdle(); + mTetherStatsProviderCb.assertNoCallback(); + + // Verify that notifyAlertReached fired when quota is reached. + when(mNetd.tetherOffloadGetStats()).thenReturn( + new TetherStatsParcel[] {buildTestTetherStatsParcel(mobileIfIndex, 50, 0, 50, 0)}); + mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS); + waitForIdle(); + mTetherStatsProviderCb.expectNotifyAlertReached(); + + // Verify that set quota with UNLIMITED won't trigger any callback. + mTetherStatsProvider.onSetAlert(QUOTA_UNLIMITED); + mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS); + waitForIdle(); + mTetherStatsProviderCb.assertNoCallback(); + } } From 7c538fa7ff359de75011389e8d8ef9d7c51c7084 Mon Sep 17 00:00:00 2001 From: Treehugger Robot Date: Tue, 9 Jun 2020 13:28:38 +0000 Subject: [PATCH 162/188] [BOT.6] Make BpfCoordinator to support data limit The BPF tethering coordinator listens to the forwarding rule changes for updating data limit which is applied in the following conditions. - After adding the first rule on a given upstream, add data limit. - After removing the last rule on a given upstream, clear data limit. - The service applies a new data limit on current upstream. The reason for relying on rule changes is because the Tethering and IpServer objects have multi-internal state machines. It is hard to synchronize all of their states. Note that the data limit cleanup for stopping or switching upstream relies on offload rules are all removed as well. Bug: 150736748 Test: manual Original-Change: https://android-review.googlesource.com/1302436 Merged-In: I829d36339973f9473fe6b616c48aa288f18d1c46 Change-Id: I829d36339973f9473fe6b616c48aa288f18d1c46 --- Tethering/src/android/net/ip/IpServer.java | 50 +-- .../tethering/BpfCoordinator.java | 341 ++++++++++++++++-- .../networkstack/tethering/Tethering.java | 4 +- .../unit/src/android/net/ip/IpServerTest.java | 166 ++++++--- .../tethering/BpfCoordinatorTest.java | 6 +- 5 files changed, 452 insertions(+), 115 deletions(-) diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java index 830f6a0fec..1671dda4bd 100644 --- a/Tethering/src/android/net/ip/IpServer.java +++ b/Tethering/src/android/net/ip/IpServer.java @@ -77,7 +77,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; -import java.util.LinkedHashMap; import java.util.List; import java.util.Objects; import java.util.Random; @@ -272,9 +271,6 @@ public class IpServer extends StateMachine { } } - private final LinkedHashMap mIpv6ForwardingRules = - new LinkedHashMap<>(); - private final IpNeighborMonitor mIpNeighborMonitor; private LinkAddress mIpv4Address; @@ -843,43 +839,29 @@ public class IpServer extends StateMachine { // TODO: Perhaps remove this protection check. if (!mUsingBpfOffload) return; - try { - mNetd.tetherOffloadRuleAdd(rule.toTetherOffloadRuleParcel()); - mIpv6ForwardingRules.put(rule.address, rule); - } catch (RemoteException | ServiceSpecificException e) { - mLog.e("Could not add IPv6 downstream rule: ", e); - } + mBpfCoordinator.tetherOffloadRuleAdd(this, rule); } - private void removeIpv6ForwardingRule(Ipv6ForwardingRule rule, boolean removeFromMap) { - // Theoretically, we don't need this check because IP neighbor monitor doesn't start if BPF - // offload is disabled. Add this check just in case. + private void removeIpv6ForwardingRule(Ipv6ForwardingRule rule) { // TODO: Perhaps remove this protection check. + // See the related comment in #addIpv6ForwardingRule. if (!mUsingBpfOffload) return; - try { - mNetd.tetherOffloadRuleRemove(rule.toTetherOffloadRuleParcel()); - if (removeFromMap) { - mIpv6ForwardingRules.remove(rule.address); - } - } catch (RemoteException | ServiceSpecificException e) { - mLog.e("Could not remove IPv6 downstream rule: ", e); - } + mBpfCoordinator.tetherOffloadRuleRemove(this, rule); } private void clearIpv6ForwardingRules() { - for (Ipv6ForwardingRule rule : mIpv6ForwardingRules.values()) { - removeIpv6ForwardingRule(rule, false /*removeFromMap*/); - } - mIpv6ForwardingRules.clear(); + if (!mUsingBpfOffload) return; + + mBpfCoordinator.tetherOffloadRuleClear(this); } - // Convenience method to replace a rule with the same rule on a new upstream interface. - // Allows replacing the rules in one iteration pass without ConcurrentModificationExceptions. - // Relies on the fact that rules are in a map indexed by IP address. - private void updateIpv6ForwardingRule(Ipv6ForwardingRule rule, int newIfindex) { - addIpv6ForwardingRule(rule.onNewUpstream(newIfindex)); - removeIpv6ForwardingRule(rule, false /*removeFromMap*/); + private void updateIpv6ForwardingRule(int newIfindex) { + // TODO: Perhaps remove this protection check. + // See the related comment in #addIpv6ForwardingRule. + if (!mUsingBpfOffload) return; + + mBpfCoordinator.tetherOffloadRuleUpdate(this, newIfindex); } // Handles all updates to IPv6 forwarding rules. These can currently change only if the upstream @@ -895,9 +877,7 @@ public class IpServer extends StateMachine { // If the upstream interface has changed, remove all rules and re-add them with the new // upstream interface. if (prevUpstreamIfindex != upstreamIfindex) { - for (Ipv6ForwardingRule rule : mIpv6ForwardingRules.values()) { - updateIpv6ForwardingRule(rule, upstreamIfindex); - } + updateIpv6ForwardingRule(upstreamIfindex); } // If we're here to process a NeighborEvent, do so now. @@ -917,7 +897,7 @@ public class IpServer extends StateMachine { if (e.isValid()) { addIpv6ForwardingRule(rule); } else { - removeIpv6ForwardingRule(rule, true /*removeFromMap*/); + removeIpv6ForwardingRule(rule); } } diff --git a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java index 089b12aa39..fc27b6add0 100644 --- a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java +++ b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java @@ -32,6 +32,7 @@ import android.net.NetworkStats; import android.net.NetworkStats.Entry; import android.net.TetherOffloadRuleParcel; import android.net.TetherStatsParcel; +import android.net.ip.IpServer; import android.net.netstats.provider.NetworkStatsProvider; import android.net.util.SharedLog; import android.net.util.TetheringUtils.ForwardedStats; @@ -48,11 +49,17 @@ import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import java.net.Inet6Address; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Objects; /** * This coordinator is responsible for providing BPF offload relevant functionality. * - Get tethering stats. + * - Set data limit. * - Set global alert. + * - Add/remove forwarding rules. * * @hide */ @@ -77,7 +84,14 @@ public class BpfCoordinator { private final Dependencies mDeps; @Nullable private final BpfTetherStatsProvider mStatsProvider; - private boolean mStarted = false; + + // Tracks whether BPF tethering is started or not. This is set by tethering before it + // starts the first IpServer and is cleared by tethering shortly before the last IpServer + // is stopped. Note that rule updates (especially deletions, but sometimes additions as + // well) may arrive when this is false. If they do, they must be communicated to netd. + // Changes in data limits may also arrive when this is false, and if they do, they must + // also be communicated to netd. + private boolean mPollingStarted = false; // Tracking remaining alert quota. Unlike limit quota is subject to interface, the alert // quota is interface independent and global for tether offload. @@ -86,13 +100,40 @@ public class BpfCoordinator { // Maps upstream interface index to offloaded traffic statistics. // Always contains the latest total bytes/packets, since each upstream was started, received // from the BPF maps for each interface. - private SparseArray mStats = new SparseArray<>(); + private final SparseArray mStats = new SparseArray<>(); + + // Maps upstream interface names to interface quotas. + // Always contains the latest value received from the framework for each interface, regardless + // of whether offload is currently running (or is even supported) on that interface. Only + // includes interfaces that have a quota set. Note that this map is used for storing the quota + // which is set from the service. Because the service uses the interface name to present the + // interface, this map uses the interface name to be the mapping index. + private final HashMap mInterfaceQuotas = new HashMap<>(); // Maps upstream interface index to interface names. // Store all interface name since boot. Used for lookup what interface name it is from the // tether stats got from netd because netd reports interface index to present an interface. // TODO: Remove the unused interface name. - private SparseArray mInterfaceNames = new SparseArray<>(); + private final SparseArray mInterfaceNames = new SparseArray<>(); + + // Map of downstream rule maps. Each of these maps represents the IPv6 forwarding rules for a + // given downstream. Each map: + // - Is owned by the IpServer that is responsible for that downstream. + // - Must only be modified by that IpServer. + // - Is created when the IpServer adds its first rule, and deleted when the IpServer deletes + // its last rule (or clears its rules). + // TODO: Perhaps seal the map and rule operations which communicates with netd into a class. + // TODO: Does this need to be a LinkedHashMap or can it just be a HashMap? Also, could it be + // a ConcurrentHashMap, in order to avoid the copies in tetherOffloadRuleClear + // and tetherOffloadRuleUpdate? + // TODO: Perhaps use one-dimensional map and access specific downstream rules via downstream + // index. For doing that, IpServer must guarantee that it always has a valid IPv6 downstream + // interface index while calling function to clear all rules. IpServer may be calling clear + // rules function without a valid IPv6 downstream interface index even if it may have one + // before. IpServer would need to call getInterfaceParams() in the constructor instead of when + // startIpv6() is called, and make mInterfaceParams final. + private final HashMap> + mIpv6ForwardingRules = new LinkedHashMap<>(); // Runnable that used by scheduling next polling of stats. private final Runnable mScheduledPollingTask = () -> { @@ -101,14 +142,15 @@ public class BpfCoordinator { }; @VisibleForTesting - static class Dependencies { + public static class Dependencies { int getPerformPollInterval() { // TODO: Consider make this configurable. return DEFAULT_PERFORM_POLL_INTERVAL_MS; } } - BpfCoordinator(@NonNull Handler handler, @NonNull INetd netd, + @VisibleForTesting + public BpfCoordinator(@NonNull Handler handler, @NonNull INetd netd, @NonNull NetworkStatsManager nsm, @NonNull SharedLog log, @NonNull Dependencies deps) { mHandler = handler; mNetd = netd; @@ -132,31 +174,153 @@ public class BpfCoordinator { * TODO: Perhaps check BPF support before starting. * TODO: Start the stats polling only if there is any client on the downstream. */ - public void start() { - if (mStarted) return; + public void startPolling() { + if (mPollingStarted) return; - mStarted = true; + mPollingStarted = true; maybeSchedulePollingStats(); - mLog.i("BPF tethering coordinator started"); + mLog.i("Polling started"); } /** - * Stop BPF tethering offload stats polling and cleanup upstream parameters. + * Stop BPF tethering offload stats polling. + * The data limit cleanup and the tether stats maps cleanup are not implemented here. + * These cleanups rely on all IpServers calling #tetherOffloadRuleRemove. After the + * last rule is removed from the upstream, #tetherOffloadRuleRemove does the cleanup + * functionality. * Note that this can be only called on handler thread. */ - public void stop() { - if (!mStarted) return; + public void stopPolling() { + if (!mPollingStarted) return; // Stop scheduled polling tasks and poll the latest stats from BPF maps. if (mHandler.hasCallbacks(mScheduledPollingTask)) { mHandler.removeCallbacks(mScheduledPollingTask); } updateForwardedStatsFromNetd(); + mPollingStarted = false; - mStarted = false; + mLog.i("Polling stopped"); + } - mLog.i("BPF tethering coordinator stopped"); + /** + * Add forwarding rule. After adding the first rule on a given upstream, must add the data + * limit on the given upstream. + * Note that this can be only called on handler thread. + */ + public void tetherOffloadRuleAdd( + @NonNull final IpServer ipServer, @NonNull final Ipv6ForwardingRule rule) { + try { + // TODO: Perhaps avoid to add a duplicate rule. + mNetd.tetherOffloadRuleAdd(rule.toTetherOffloadRuleParcel()); + } catch (RemoteException | ServiceSpecificException e) { + mLog.e("Could not add IPv6 forwarding rule: ", e); + return; + } + + if (!mIpv6ForwardingRules.containsKey(ipServer)) { + mIpv6ForwardingRules.put(ipServer, new LinkedHashMap()); + } + LinkedHashMap rules = mIpv6ForwardingRules.get(ipServer); + + // Setup the data limit on the given upstream if the first rule is added. + final int upstreamIfindex = rule.upstreamIfindex; + if (!isAnyRuleOnUpstream(upstreamIfindex)) { + // If failed to set a data limit, probably should not use this upstream, because + // the upstream may not want to blow through the data limit that was told to apply. + // TODO: Perhaps stop the coordinator. + boolean success = updateDataLimit(upstreamIfindex); + if (!success) { + final String iface = mInterfaceNames.get(upstreamIfindex); + mLog.e("Setting data limit for " + iface + " failed."); + } + } + + // Must update the adding rule after calling #isAnyRuleOnUpstream because it needs to + // check if it is about adding a first rule for a given upstream. + rules.put(rule.address, rule); + } + + /** + * Remove forwarding rule. After removing the last rule on a given upstream, must clear + * data limit, update the last tether stats and remove the tether stats in the BPF maps. + * Note that this can be only called on handler thread. + */ + public void tetherOffloadRuleRemove( + @NonNull final IpServer ipServer, @NonNull final Ipv6ForwardingRule rule) { + try { + // TODO: Perhaps avoid to remove a non-existent rule. + mNetd.tetherOffloadRuleRemove(rule.toTetherOffloadRuleParcel()); + } catch (RemoteException | ServiceSpecificException e) { + mLog.e("Could not remove IPv6 forwarding rule: ", e); + return; + } + + LinkedHashMap rules = mIpv6ForwardingRules.get(ipServer); + if (rules == null) return; + + // Must remove rules before calling #isAnyRuleOnUpstream because it needs to check if + // the last rule is removed for a given upstream. If no rule is removed, return early. + // Avoid unnecessary work on a non-existent rule which may have never been added or + // removed already. + if (rules.remove(rule.address) == null) return; + + // Remove the downstream entry if it has no more rule. + if (rules.isEmpty()) { + mIpv6ForwardingRules.remove(ipServer); + } + + // Do cleanup functionality if there is no more rule on the given upstream. + final int upstreamIfindex = rule.upstreamIfindex; + if (!isAnyRuleOnUpstream(upstreamIfindex)) { + try { + final TetherStatsParcel stats = + mNetd.tetherOffloadGetAndClearStats(upstreamIfindex); + // Update the last stats delta and delete the local cache for a given upstream. + updateQuotaAndStatsFromSnapshot(new TetherStatsParcel[] {stats}); + mStats.remove(upstreamIfindex); + } catch (RemoteException | ServiceSpecificException e) { + Log.wtf(TAG, "Exception when cleanup tether stats for upstream index " + + upstreamIfindex + ": ", e); + } + } + } + + /** + * Clear all forwarding rules for a given downstream. + * Note that this can be only called on handler thread. + */ + public void tetherOffloadRuleClear(@NonNull final IpServer ipServer) { + final LinkedHashMap rules = mIpv6ForwardingRules.get( + ipServer); + if (rules == null) return; + + // Need to build a rule list because the rule map may be changed in the iteration. + for (final Ipv6ForwardingRule rule : new ArrayList(rules.values())) { + tetherOffloadRuleRemove(ipServer, rule); + } + } + + /** + * Update existing forwarding rules to new upstream for a given downstream. + * Note that this can be only called on handler thread. + */ + public void tetherOffloadRuleUpdate(@NonNull final IpServer ipServer, int newUpstreamIfindex) { + final LinkedHashMap rules = mIpv6ForwardingRules.get( + ipServer); + if (rules == null) return; + + // Need to build a rule list because the rule map may be changed in the iteration. + for (final Ipv6ForwardingRule rule : new ArrayList(rules.values())) { + // Remove the old rule before adding the new one because the map uses the same key for + // both rules. Reversing the processing order causes that the new rule is removed as + // unexpected. + // TODO: Add new rule first to reduce the latency which has no rule. + tetherOffloadRuleRemove(ipServer, rule); + tetherOffloadRuleAdd(ipServer, rule.onNewUpstream(newUpstreamIfindex)); + } } /** @@ -184,12 +348,17 @@ public class BpfCoordinator { public static class Ipv6ForwardingRule { public final int upstreamIfindex; public final int downstreamIfindex; + + @NonNull public final Inet6Address address; + @NonNull public final MacAddress srcMac; + @NonNull public final MacAddress dstMac; - public Ipv6ForwardingRule(int upstreamIfindex, int downstreamIfIndex, Inet6Address address, - MacAddress srcMac, MacAddress dstMac) { + public Ipv6ForwardingRule(int upstreamIfindex, int downstreamIfIndex, + @NonNull Inet6Address address, @NonNull MacAddress srcMac, + @NonNull MacAddress dstMac) { this.upstreamIfindex = upstreamIfindex; this.downstreamIfindex = downstreamIfIndex; this.address = address; @@ -198,6 +367,7 @@ public class BpfCoordinator { } /** Return a new rule object which updates with new upstream index. */ + @NonNull public Ipv6ForwardingRule onNewUpstream(int newUpstreamIfindex) { return new Ipv6ForwardingRule(newUpstreamIfindex, downstreamIfindex, address, srcMac, dstMac); @@ -207,6 +377,7 @@ public class BpfCoordinator { * Don't manipulate TetherOffloadRuleParcel directly because implementing onNewUpstream() * would be error-prone due to generated stable AIDL classes not having a copy constructor. */ + @NonNull public TetherOffloadRuleParcel toTetherOffloadRuleParcel() { final TetherOffloadRuleParcel parcel = new TetherOffloadRuleParcel(); parcel.inputInterfaceIndex = upstreamIfindex; @@ -217,6 +388,24 @@ public class BpfCoordinator { parcel.dstL2Address = dstMac.toByteArray(); return parcel; } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Ipv6ForwardingRule)) return false; + Ipv6ForwardingRule that = (Ipv6ForwardingRule) o; + return this.upstreamIfindex == that.upstreamIfindex + && this.downstreamIfindex == that.downstreamIfindex + && Objects.equals(this.address, that.address) + && Objects.equals(this.srcMac, that.srcMac) + && Objects.equals(this.dstMac, that.dstMac); + } + + @Override + public int hashCode() { + // TODO: if this is ever used in production code, don't pass ifindices + // to Objects.hash() to avoid autoboxing overhead. + return Objects.hash(upstreamIfindex, downstreamIfindex, address, srcMac, dstMac); + } } /** @@ -245,7 +434,22 @@ public class BpfCoordinator { @Override public void onSetLimit(@NonNull String iface, long quotaBytes) { - // no-op + if (quotaBytes < QUOTA_UNLIMITED) { + throw new IllegalArgumentException("invalid quota value " + quotaBytes); + } + + mHandler.post(() -> { + final Long curIfaceQuota = mInterfaceQuotas.get(iface); + + if (null == curIfaceQuota && QUOTA_UNLIMITED == quotaBytes) return; + + if (quotaBytes == QUOTA_UNLIMITED) { + mInterfaceQuotas.remove(iface); + } else { + mInterfaceQuotas.put(iface, quotaBytes); + } + maybeUpdateDataLimit(iface); + }); } @VisibleForTesting @@ -270,9 +474,79 @@ public class BpfCoordinator { } } + private int getInterfaceIndexFromRules(@NonNull String ifName) { + for (LinkedHashMap rules : mIpv6ForwardingRules + .values()) { + for (Ipv6ForwardingRule rule : rules.values()) { + final int upstreamIfindex = rule.upstreamIfindex; + if (TextUtils.equals(ifName, mInterfaceNames.get(upstreamIfindex))) { + return upstreamIfindex; + } + } + } + return 0; + } + + private long getQuotaBytes(@NonNull String iface) { + final Long limit = mInterfaceQuotas.get(iface); + final long quotaBytes = (limit != null) ? limit : QUOTA_UNLIMITED; + + return quotaBytes; + } + + private boolean sendDataLimitToNetd(int ifIndex, long quotaBytes) { + if (ifIndex == 0) { + Log.wtf(TAG, "Invalid interface index."); + return false; + } + + try { + mNetd.tetherOffloadSetInterfaceQuota(ifIndex, quotaBytes); + } catch (RemoteException | ServiceSpecificException e) { + mLog.e("Exception when updating quota " + quotaBytes + ": ", e); + return false; + } + + return true; + } + + // Handle the data limit update from the service which is the stats provider registered for. + private void maybeUpdateDataLimit(@NonNull String iface) { + // Set data limit only on a given upstream which has at least one rule. If we can't get + // an interface index for a given interface name, it means either there is no rule for + // a given upstream or the interface name is not an upstream which is monitored by the + // coordinator. + final int ifIndex = getInterfaceIndexFromRules(iface); + if (ifIndex == 0) return; + + final long quotaBytes = getQuotaBytes(iface); + sendDataLimitToNetd(ifIndex, quotaBytes); + } + + // Handle the data limit update while adding forwarding rules. + private boolean updateDataLimit(int ifIndex) { + final String iface = mInterfaceNames.get(ifIndex); + if (iface == null) { + mLog.e("Fail to get the interface name for index " + ifIndex); + return false; + } + final long quotaBytes = getQuotaBytes(iface); + return sendDataLimitToNetd(ifIndex, quotaBytes); + } + + private boolean isAnyRuleOnUpstream(int upstreamIfindex) { + for (LinkedHashMap rules : mIpv6ForwardingRules + .values()) { + for (Ipv6ForwardingRule rule : rules.values()) { + if (upstreamIfindex == rule.upstreamIfindex) return true; + } + } + return false; + } + @NonNull private NetworkStats buildNetworkStats(@NonNull StatsType type, int ifIndex, - @NonNull ForwardedStats diff) { + @NonNull final ForwardedStats diff) { NetworkStats stats = new NetworkStats(0L, 0); final String iface = mInterfaceNames.get(ifIndex); if (iface == null) { @@ -302,17 +576,8 @@ public class BpfCoordinator { } } - private void updateForwardedStatsFromNetd() { - final TetherStatsParcel[] tetherStatsList; - try { - // The reported tether stats are total data usage for all currently-active upstream - // interfaces since tethering start. - tetherStatsList = mNetd.tetherOffloadGetStats(); - } catch (RemoteException | ServiceSpecificException e) { - mLog.e("Problem fetching tethering stats: ", e); - return; - } - + private void updateQuotaAndStatsFromSnapshot( + @NonNull final TetherStatsParcel[] tetherStatsList) { long usedAlertQuota = 0; for (TetherStatsParcel tetherStats : tetherStatsList) { final Integer ifIndex = tetherStats.ifIndex; @@ -332,7 +597,7 @@ public class BpfCoordinator { buildNetworkStats(StatsType.STATS_PER_IFACE, ifIndex, diff), buildNetworkStats(StatsType.STATS_PER_UID, ifIndex, diff)); } catch (ArrayIndexOutOfBoundsException e) { - Log.wtf("Fail to update the accumulated stats delta for interface index " + Log.wtf(TAG, "Fail to update the accumulated stats delta for interface index " + ifIndex + " : ", e); } } @@ -344,10 +609,24 @@ public class BpfCoordinator { updateAlertQuota(newQuota); } + // TODO: Count the used limit quota for notifying data limit reached. + } + + private void updateForwardedStatsFromNetd() { + final TetherStatsParcel[] tetherStatsList; + try { + // The reported tether stats are total data usage for all currently-active upstream + // interfaces since tethering start. + tetherStatsList = mNetd.tetherOffloadGetStats(); + } catch (RemoteException | ServiceSpecificException e) { + mLog.e("Problem fetching tethering stats: ", e); + return; + } + updateQuotaAndStatsFromSnapshot(tetherStatsList); } private void maybeSchedulePollingStats() { - if (!mStarted) return; + if (!mPollingStarted) return; if (mHandler.hasCallbacks(mScheduledPollingTask)) { mHandler.removeCallbacks(mScheduledPollingTask); diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java index cfe9feade8..df67458550 100644 --- a/Tethering/src/com/android/networkstack/tethering/Tethering.java +++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java @@ -1709,7 +1709,7 @@ public class Tethering { } // TODO: Check the upstream interface if it is managed by BPF offload. - mBpfCoordinator.start(); + mBpfCoordinator.startPolling(); } @Override @@ -1722,7 +1722,7 @@ public class Tethering { mTetherUpstream = null; reportUpstreamChanged(null); } - mBpfCoordinator.stop(); + mBpfCoordinator.stopPolling(); } private boolean updateUpstreamWanted() { diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java index 433aacfaff..c3bc915a23 100644 --- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java +++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java @@ -54,12 +54,14 @@ import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import android.app.usage.NetworkStatsManager; import android.net.INetd; import android.net.InetAddresses; import android.net.InterfaceConfigurationParcel; @@ -69,6 +71,7 @@ import android.net.LinkProperties; import android.net.MacAddress; import android.net.RouteInfo; import android.net.TetherOffloadRuleParcel; +import android.net.TetherStatsParcel; import android.net.dhcp.DhcpServingParamsParcel; import android.net.dhcp.IDhcpEventCallbacks; import android.net.dhcp.IDhcpServer; @@ -80,14 +83,17 @@ import android.net.util.InterfaceParams; import android.net.util.InterfaceSet; import android.net.util.PrefixUtils; import android.net.util.SharedLog; +import android.os.Handler; import android.os.RemoteException; import android.os.test.TestLooper; import android.text.TextUtils; +import androidx.annotation.NonNull; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.networkstack.tethering.BpfCoordinator; +import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule; import com.android.networkstack.tethering.PrivateAddressCoordinator; import org.junit.Before; @@ -101,6 +107,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.net.Inet4Address; +import java.net.Inet6Address; import java.net.InetAddress; import java.util.Arrays; import java.util.List; @@ -127,7 +134,6 @@ public class IpServerTest { private final IpPrefix mBluetoothPrefix = new IpPrefix("192.168.44.0/24"); @Mock private INetd mNetd; - @Mock private BpfCoordinator mBpfCoordinator; @Mock private IpServer.Callback mCallback; @Mock private SharedLog mSharedLog; @Mock private IDhcpServer mDhcpServer; @@ -135,6 +141,7 @@ public class IpServerTest { @Mock private IpNeighborMonitor mIpNeighborMonitor; @Mock private IpServer.Dependencies mDependencies; @Mock private PrivateAddressCoordinator mAddressCoordinator; + @Mock private NetworkStatsManager mStatsManager; @Captor private ArgumentCaptor mDhcpParamsCaptor; @@ -144,6 +151,7 @@ public class IpServerTest { private IpServer mIpServer; private InterfaceConfigurationParcel mInterfaceConfiguration; private NeighborEventConsumer mNeighborEventConsumer; + private BpfCoordinator mBpfCoordinator; private void initStateMachine(int interfaceType) throws Exception { initStateMachine(interfaceType, false /* usingLegacyDhcp */, DEFAULT_USING_BPF_OFFLOAD); @@ -217,6 +225,10 @@ public class IpServerTest { MockitoAnnotations.initMocks(this); when(mSharedLog.forSubComponent(anyString())).thenReturn(mSharedLog); when(mAddressCoordinator.requestDownstreamAddress(any())).thenReturn(mTestAddress); + + BpfCoordinator bc = new BpfCoordinator(new Handler(mLooper.getLooper()), mNetd, + mStatsManager, mSharedLog, new BpfCoordinator.Dependencies()); + mBpfCoordinator = spy(bc); } @Test @@ -621,6 +633,10 @@ public class IpServerTest { * (actual: "android.net.TetherOffloadRuleParcel@8c827b0" or some such), but at least it does * work. * + * TODO: consider making the error message more readable by adding a method that catching the + * AssertionFailedError and throwing a new assertion with more details. See + * NetworkMonitorTest#verifyNetworkTested. + * * See ConnectivityServiceTest#assertRoutesAdded for an alternative approach which solves the * TooManyActualInvocations problem described above by forcing the caller of the custom assert * method to specify all expected invocations in one call. This is useful when the stable @@ -660,6 +676,27 @@ public class IpServerTest { return argThat(new TetherOffloadRuleParcelMatcher(upstreamIfindex, dst, dstMac)); } + private static Ipv6ForwardingRule makeForwardingRule( + int upstreamIfindex, @NonNull InetAddress dst, @NonNull MacAddress dstMac) { + return new Ipv6ForwardingRule(upstreamIfindex, TEST_IFACE_PARAMS.index, + (Inet6Address) dst, TEST_IFACE_PARAMS.macAddr, dstMac); + } + + private TetherStatsParcel buildEmptyTetherStatsParcel(int ifIndex) { + TetherStatsParcel parcel = new TetherStatsParcel(); + parcel.ifIndex = ifIndex; + return parcel; + } + + private void resetNetdAndBpfCoordinator() throws Exception { + reset(mNetd, mBpfCoordinator); + when(mNetd.tetherOffloadGetStats()).thenReturn(new TetherStatsParcel[0]); + when(mNetd.tetherOffloadGetAndClearStats(UPSTREAM_IFINDEX)) + .thenReturn(buildEmptyTetherStatsParcel(UPSTREAM_IFINDEX)); + when(mNetd.tetherOffloadGetAndClearStats(UPSTREAM_IFINDEX2)) + .thenReturn(buildEmptyTetherStatsParcel(UPSTREAM_IFINDEX2)); + } + @Test public void addRemoveipv6ForwardingRules() throws Exception { initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */, @@ -677,75 +714,100 @@ public class IpServerTest { final MacAddress macA = MacAddress.fromString("00:00:00:00:00:0a"); final MacAddress macB = MacAddress.fromString("11:22:33:00:00:0b"); - reset(mNetd); + resetNetdAndBpfCoordinator(); + verifyNoMoreInteractions(mBpfCoordinator, mNetd); + + // TODO: Perhaps verify the interaction of tetherOffloadSetInterfaceQuota and + // tetherOffloadGetAndClearStats in netd while the rules are changed. // Events on other interfaces are ignored. recvNewNeigh(notMyIfindex, neighA, NUD_REACHABLE, macA); - verifyNoMoreInteractions(mNetd); + verifyNoMoreInteractions(mBpfCoordinator, mNetd); // Events on this interface are received and sent to netd. recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA); + verify(mBpfCoordinator).tetherOffloadRuleAdd( + mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighA, macA)); verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA)); - reset(mNetd); + resetNetdAndBpfCoordinator(); recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB); + verify(mBpfCoordinator).tetherOffloadRuleAdd( + mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighB, macB)); verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighB, macB)); - reset(mNetd); + resetNetdAndBpfCoordinator(); // Link-local and multicast neighbors are ignored. recvNewNeigh(myIfindex, neighLL, NUD_REACHABLE, macA); - verifyNoMoreInteractions(mNetd); + verifyNoMoreInteractions(mBpfCoordinator, mNetd); recvNewNeigh(myIfindex, neighMC, NUD_REACHABLE, macA); - verifyNoMoreInteractions(mNetd); + verifyNoMoreInteractions(mBpfCoordinator, mNetd); // A neighbor that is no longer valid causes the rule to be removed. // NUD_FAILED events do not have a MAC address. recvNewNeigh(myIfindex, neighA, NUD_FAILED, null); + verify(mBpfCoordinator).tetherOffloadRuleRemove( + mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighA, macNull)); verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macNull)); - reset(mNetd); + resetNetdAndBpfCoordinator(); // A neighbor that is deleted causes the rule to be removed. recvDelNeigh(myIfindex, neighB, NUD_STALE, macB); + verify(mBpfCoordinator).tetherOffloadRuleRemove( + mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighB, macNull)); verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macNull)); - reset(mNetd); + resetNetdAndBpfCoordinator(); - // Upstream changes result in deleting and re-adding the rules. + // Upstream changes result in updating the rules. recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA); recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB); - reset(mNetd); + resetNetdAndBpfCoordinator(); InOrder inOrder = inOrder(mNetd); LinkProperties lp = new LinkProperties(); lp.setInterfaceName(UPSTREAM_IFACE2); dispatchTetherConnectionChanged(UPSTREAM_IFACE2, lp, -1); - inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX2, neighA, macA)); + verify(mBpfCoordinator).tetherOffloadRuleUpdate(mIpServer, UPSTREAM_IFINDEX2); inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macA)); - inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX2, neighB, macB)); + inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX2, neighA, macA)); inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB)); - reset(mNetd); + inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX2, neighB, macB)); + resetNetdAndBpfCoordinator(); // When the upstream is lost, rules are removed. dispatchTetherConnectionChanged(null, null, 0); + // Clear function is called two times by: + // - processMessage CMD_TETHER_CONNECTION_CHANGED for the upstream is lost. + // - processMessage CMD_IPV6_TETHER_UPDATE for the IPv6 upstream is lost. + // See dispatchTetherConnectionChanged. + verify(mBpfCoordinator, times(2)).tetherOffloadRuleClear(mIpServer); verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX2, neighA, macA)); verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX2, neighB, macB)); - reset(mNetd); + resetNetdAndBpfCoordinator(); // If the upstream is IPv4-only, no rules are added. dispatchTetherConnectionChanged(UPSTREAM_IFACE); - reset(mNetd); + resetNetdAndBpfCoordinator(); recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA); - verifyNoMoreInteractions(mNetd); + // Clear function is called by #updateIpv6ForwardingRules for the IPv6 upstream is lost. + verify(mBpfCoordinator).tetherOffloadRuleClear(mIpServer); + verifyNoMoreInteractions(mBpfCoordinator, mNetd); // Rules can be added again once upstream IPv6 connectivity is available. lp.setInterfaceName(UPSTREAM_IFACE); dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, -1); recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB); + verify(mBpfCoordinator).tetherOffloadRuleAdd( + mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighB, macB)); verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighB, macB)); + verify(mBpfCoordinator, never()).tetherOffloadRuleAdd( + mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighA, macA)); verify(mNetd, never()).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA)); // If upstream IPv6 connectivity is lost, rules are removed. - reset(mNetd); + resetNetdAndBpfCoordinator(); dispatchTetherConnectionChanged(UPSTREAM_IFACE, null, 0); + verify(mBpfCoordinator).tetherOffloadRuleClear(mIpServer); verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB)); // When the interface goes down, rules are removed. @@ -753,15 +815,20 @@ public class IpServerTest { dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, -1); recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA); recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB); + verify(mBpfCoordinator).tetherOffloadRuleAdd( + mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighA, macA)); verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA)); + verify(mBpfCoordinator).tetherOffloadRuleAdd( + mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighB, macB)); verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighB, macB)); - reset(mNetd); + resetNetdAndBpfCoordinator(); mIpServer.stop(); mLooper.dispatchAll(); + verify(mBpfCoordinator).tetherOffloadRuleClear(mIpServer); verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macA)); verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB)); - reset(mNetd); + resetNetdAndBpfCoordinator(); } @Test @@ -771,35 +838,46 @@ public class IpServerTest { final MacAddress macA = MacAddress.fromString("00:00:00:00:00:0a"); final MacAddress macNull = MacAddress.fromString("00:00:00:00:00:00"); - reset(mNetd); - // Expect that rules can be only added/removed when the BPF offload config is enabled. - // Note that the usingBpfOffload false case is not a realistic test case. Because IP + // Note that the BPF offload disabled case is not a realistic test case. Because IP // neighbor monitor doesn't start if BPF offload is disabled, there should have no // neighbor event listening. This is used for testing the protection check just in case. - // TODO: Perhaps remove this test once we don't need this check anymore. - for (boolean usingBpfOffload : new boolean[]{true, false}) { - initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */, - usingBpfOffload); + // TODO: Perhaps remove the BPF offload disabled case test once this check isn't needed + // anymore. - // A neighbor is added. - recvNewNeigh(myIfindex, neigh, NUD_REACHABLE, macA); - if (usingBpfOffload) { - verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neigh, macA)); - } else { - verify(mNetd, never()).tetherOffloadRuleAdd(any()); - } - reset(mNetd); + // [1] Enable BPF offload. + // A neighbor that is added or deleted causes the rule to be added or removed. + initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */, + true /* usingBpfOffload */); + resetNetdAndBpfCoordinator(); - // A neighbor is deleted. - recvDelNeigh(myIfindex, neigh, NUD_STALE, macA); - if (usingBpfOffload) { - verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neigh, macNull)); - } else { - verify(mNetd, never()).tetherOffloadRuleRemove(any()); - } - reset(mNetd); - } + recvNewNeigh(myIfindex, neigh, NUD_REACHABLE, macA); + verify(mBpfCoordinator).tetherOffloadRuleAdd( + mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neigh, macA)); + verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neigh, macA)); + resetNetdAndBpfCoordinator(); + + recvDelNeigh(myIfindex, neigh, NUD_STALE, macA); + verify(mBpfCoordinator).tetherOffloadRuleRemove( + mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neigh, macNull)); + verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neigh, macNull)); + resetNetdAndBpfCoordinator(); + + // [2] Disable BPF offload. + // A neighbor that is added or deleted doesn’t cause the rule to be added or removed. + initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */, + false /* usingBpfOffload */); + resetNetdAndBpfCoordinator(); + + recvNewNeigh(myIfindex, neigh, NUD_REACHABLE, macA); + verify(mBpfCoordinator, never()).tetherOffloadRuleAdd(any(), any()); + verify(mNetd, never()).tetherOffloadRuleAdd(any()); + resetNetdAndBpfCoordinator(); + + recvDelNeigh(myIfindex, neigh, NUD_STALE, macA); + verify(mBpfCoordinator, never()).tetherOffloadRuleRemove(any(), any()); + verify(mNetd, never()).tetherOffloadRuleRemove(any()); + resetNetdAndBpfCoordinator(); } @Test diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java index 3e19ddfc0b..e2d7aab4e3 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java @@ -141,7 +141,7 @@ public class BpfCoordinatorTest { setupFunctioningNetdInterface(); final BpfCoordinator coordinator = makeBpfCoordinator(); - coordinator.start(); + coordinator.startPolling(); final String wlanIface = "wlan0"; final Integer wlanIfIndex = 100; @@ -197,7 +197,7 @@ public class BpfCoordinatorTest { // [3] Stop coordinator. // Shutdown the coordinator and clear the invocation history, especially the // tetherOffloadGetStats() calls. - coordinator.stop(); + coordinator.stopPolling(); clearInvocations(mNetd); // Verify the polling update thread stopped. @@ -211,7 +211,7 @@ public class BpfCoordinatorTest { setupFunctioningNetdInterface(); final BpfCoordinator coordinator = makeBpfCoordinator(); - coordinator.start(); + coordinator.startPolling(); final String mobileIface = "rmnet_data0"; final Integer mobileIfIndex = 100; From 5a07338239add942240a43be509d427f84b636ab Mon Sep 17 00:00:00 2001 From: paulhu Date: Wed, 27 May 2020 00:13:01 +0800 Subject: [PATCH 163/188] Make tether settings intent explicit Currently tethering notification is sending a pending intent for redirecting user to tether settings page. However, this intent is implicit that only create with Settings.ACTION_TETHER_SETTINGS. For security reasons, this intetnt should specify an explicit component to be delivered to. Thus, specify the settings package name to this intent. Test: atest TetheringTests Bug: 156353008 Change-Id: I49187aee8a004caa890e2a73c0a28d280215c7d4 Merged-In: I49187aee8a004caa890e2a73c0a28d280215c7d4 --- .../TetheringNotificationUpdater.java | 22 +++++++++++---- .../TetheringNotificationUpdaterTest.kt | 27 +++++++++++++++++++ 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java b/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java index d03deda37f..593d04a06b 100644 --- a/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java @@ -24,8 +24,10 @@ import android.app.Notification.Action; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.Resources; import android.net.NetworkCapabilities; @@ -252,6 +254,14 @@ public class TetheringNotificationUpdater { mNotificationManager.cancel(null /* tag */, id); } + @VisibleForTesting + static String getSettingsPackageName(@NonNull final PackageManager pm) { + final Intent settingsIntent = new Intent(Settings.ACTION_SETTINGS); + final ComponentName settingsComponent = settingsIntent.resolveActivity(pm); + return settingsComponent != null + ? settingsComponent.getPackageName() : "com.android.settings"; + } + @VisibleForTesting void notifyTetheringDisabledByRestriction() { final Resources res = getResourcesForSubId(mContext, mActiveDataSubId); @@ -262,8 +272,9 @@ public class TetheringNotificationUpdater { final PendingIntent pi = PendingIntent.getActivity( mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */), 0 /* requestCode */, - new Intent(Settings.ACTION_TETHER_SETTINGS), - Intent.FLAG_ACTIVITY_NEW_TASK, + new Intent(Settings.ACTION_TETHER_SETTINGS) + .setPackage(getSettingsPackageName(mContext.getPackageManager())), + Intent.FLAG_ACTIVITY_NEW_TASK | PendingIntent.FLAG_IMMUTABLE, null /* options */); showNotification(R.drawable.stat_sys_tether_general, title, message, @@ -284,7 +295,7 @@ public class TetheringNotificationUpdater { mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */), 0 /* requestCode */, intent, - 0 /* flags */); + PendingIntent.FLAG_IMMUTABLE); final Action action = new Action.Builder(NO_ICON_ID, disableButton, pi).build(); showNotification(R.drawable.stat_sys_tether_general, title, message, @@ -305,8 +316,9 @@ public class TetheringNotificationUpdater { final PendingIntent pi = PendingIntent.getActivity( mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */), 0 /* requestCode */, - new Intent(Settings.ACTION_TETHER_SETTINGS), - Intent.FLAG_ACTIVITY_NEW_TASK, + new Intent(Settings.ACTION_TETHER_SETTINGS) + .setPackage(getSettingsPackageName(mContext.getPackageManager())), + Intent.FLAG_ACTIVITY_NEW_TASK | PendingIntent.FLAG_IMMUTABLE, null /* options */); showNotification(R.drawable.stat_sys_tether_general, title, message, diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt index 7d5471f770..4b6bbac051 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt @@ -19,6 +19,10 @@ package com.android.networkstack.tethering import android.app.Notification import android.app.NotificationManager import android.content.Context +import android.content.pm.ActivityInfo +import android.content.pm.ApplicationInfo +import android.content.pm.PackageManager +import android.content.pm.ResolveInfo import android.content.res.Resources import android.net.ConnectivityManager.TETHERING_WIFI import android.os.Handler @@ -51,6 +55,7 @@ import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.eq import org.mockito.Mock import org.mockito.Mockito.doReturn +import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.reset import org.mockito.Mockito.times @@ -351,4 +356,26 @@ class TetheringNotificationUpdaterTest { notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES) verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID)) } + + @Test + fun testGetSettingsPackageName() { + val defaultSettingsPackageName = "com.android.settings" + val testSettingsPackageName = "com.android.test.settings" + val pm = mock(PackageManager::class.java) + doReturn(null).`when`(pm).resolveActivity(any(), anyInt()) + assertEquals(defaultSettingsPackageName, + TetheringNotificationUpdater.getSettingsPackageName(pm)) + + val resolveInfo = ResolveInfo().apply { + activityInfo = ActivityInfo().apply { + name = "test" + applicationInfo = ApplicationInfo().apply { + packageName = testSettingsPackageName + } + } + } + doReturn(resolveInfo).`when`(pm).resolveActivity(any(), anyInt()) + assertEquals(testSettingsPackageName, + TetheringNotificationUpdater.getSettingsPackageName(pm)) + } } From 217d7b01f87dcc960572bb88e7ec544d5ce1dc43 Mon Sep 17 00:00:00 2001 From: markchien Date: Mon, 8 Jun 2020 11:30:18 +0800 Subject: [PATCH 164/188] Pass entitlement configuration to Settings for entitlement check Tethering resource configuration is move from framwork to tethering module. Since tethering resource would not be accessible from outside of tethering module, EntitlementManager would tell Settings the entitlement configuration via intent extra when run entitlement check. Bug: 146918263 Test: atest TetheringTests Change-Id: I6f23553bb1da5f0b767f920b32a86fafb9e00b9e --- .../src/android/net/TetheringConstants.java | 38 +++++++++++-- .../tethering/EntitlementManager.java | 33 +++++++----- .../tethering/TetheringConfiguration.java | 10 ++-- .../tethering/EntitlementManagerTest.java | 53 ++++++++++++++++++- .../tethering/TetheringConfigurationTest.java | 10 +++- 5 files changed, 122 insertions(+), 22 deletions(-) diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java b/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java index fd6f171487..f14def6a3a 100644 --- a/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java +++ b/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java @@ -37,8 +37,8 @@ public final class TetheringConstants { private TetheringConstants() { } /** - * Extra used for communicating with the TetherService. Includes the type of tethering to - * enable if any. + * Extra used for communicating with the TetherService and TetherProvisioningActivity. + * Includes the type of tethering to enable if any. */ public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType"; /** @@ -56,8 +56,38 @@ public final class TetheringConstants { */ public static final String EXTRA_RUN_PROVISION = "extraRunProvision"; /** - * Extra used for communicating with the TetherService. Contains the {@link ResultReceiver} - * which will receive provisioning results. Can be left empty. + * Extra used for communicating with the TetherService and TetherProvisioningActivity. + * Contains the {@link ResultReceiver} which will receive provisioning results. + * Can not be empty. */ public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback"; + + /** + * Extra used for communicating with the TetherService and TetherProvisioningActivity. + * Contains the subId of current active cellular upstream. + * @hide + */ + public static final String EXTRA_TETHER_SUBID = "android.net.extra.TETHER_SUBID"; + + /** + * Extra used for telling TetherProvisioningActivity the entitlement package name and class + * name to start UI entitlement check. + * @hide + */ + public static final String EXTRA_TETHER_UI_PROVISIONING_APP_NAME = + "android.net.extra.TETHER_UI_PROVISIONING_APP_NAME"; + + /** + * Extra used for telling TetherService the intent action to start silent entitlement check. + * @hide + */ + public static final String EXTRA_TETHER_SILENT_PROVISIONING_ACTION = + "android.net.extra.TETHER_SILENT_PROVISIONING_ACTION"; + + /** + * Extra used for TetherService to receive the response of provisioning check. + * @hide + */ + public static final String EXTRA_TETHER_PROVISIONING_RESPONSE = + "android.net.extra.TETHER_PROVISIONING_RESPONSE"; } diff --git a/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java b/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java index 3c6e8d88ed..9dace709d7 100644 --- a/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java +++ b/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java @@ -19,6 +19,10 @@ package com.android.networkstack.tethering; import static android.net.TetheringConstants.EXTRA_ADD_TETHER_TYPE; import static android.net.TetheringConstants.EXTRA_PROVISION_CALLBACK; import static android.net.TetheringConstants.EXTRA_RUN_PROVISION; +import static android.net.TetheringConstants.EXTRA_TETHER_PROVISIONING_RESPONSE; +import static android.net.TetheringConstants.EXTRA_TETHER_SILENT_PROVISIONING_ACTION; +import static android.net.TetheringConstants.EXTRA_TETHER_SUBID; +import static android.net.TetheringConstants.EXTRA_TETHER_UI_PROVISIONING_APP_NAME; import static android.net.TetheringManager.TETHERING_BLUETOOTH; import static android.net.TetheringManager.TETHERING_ETHERNET; import static android.net.TetheringManager.TETHERING_INVALID; @@ -69,7 +73,6 @@ public class EntitlementManager { protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning"; private static final String ACTION_PROVISIONING_ALARM = "com.android.networkstack.tethering.PROVISIONING_RECHECK_ALARM"; - private static final String EXTRA_SUBID = "subId"; private final ComponentName mSilentProvisioningService; private static final int MS_PER_HOUR = 60 * 60 * 1000; @@ -197,9 +200,9 @@ public class EntitlementManager { // till upstream change to cellular. if (mUsingCellularAsUpstream) { if (showProvisioningUi) { - runUiTetherProvisioning(downstreamType, config.activeDataSubId); + runUiTetherProvisioning(downstreamType, config); } else { - runSilentTetherProvisioning(downstreamType, config.activeDataSubId); + runSilentTetherProvisioning(downstreamType, config); } mNeedReRunProvisioningUi = false; } else { @@ -262,9 +265,9 @@ public class EntitlementManager { if (mCurrentEntitlementResults.indexOfKey(downstream) < 0) { if (mNeedReRunProvisioningUi) { mNeedReRunProvisioningUi = false; - runUiTetherProvisioning(downstream, config.activeDataSubId); + runUiTetherProvisioning(downstream, config); } else { - runSilentTetherProvisioning(downstream, config.activeDataSubId); + runSilentTetherProvisioning(downstream, config); } } } @@ -361,7 +364,7 @@ public class EntitlementManager { * @param subId default data subscription ID. */ @VisibleForTesting - protected void runSilentTetherProvisioning(int type, int subId) { + protected Intent runSilentTetherProvisioning(int type, final TetheringConfiguration config) { if (DBG) mLog.i("runSilentTetherProvisioning: " + type); // For silent provisioning, settings would stop tethering when entitlement fail. ResultReceiver receiver = buildProxyReceiver(type, false/* notifyFail */, null); @@ -369,17 +372,20 @@ public class EntitlementManager { Intent intent = new Intent(); intent.putExtra(EXTRA_ADD_TETHER_TYPE, type); intent.putExtra(EXTRA_RUN_PROVISION, true); + intent.putExtra(EXTRA_TETHER_SILENT_PROVISIONING_ACTION, config.provisioningAppNoUi); + intent.putExtra(EXTRA_TETHER_PROVISIONING_RESPONSE, config.provisioningResponse); intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver); - intent.putExtra(EXTRA_SUBID, subId); + intent.putExtra(EXTRA_TETHER_SUBID, config.activeDataSubId); intent.setComponent(mSilentProvisioningService); // Only admin user can change tethering and SilentTetherProvisioning don't need to // show UI, it is fine to always start setting's background service as system user. mContext.startService(intent); + return intent; } - private void runUiTetherProvisioning(int type, int subId) { + private void runUiTetherProvisioning(int type, final TetheringConfiguration config) { ResultReceiver receiver = buildProxyReceiver(type, true/* notifyFail */, null); - runUiTetherProvisioning(type, subId, receiver); + runUiTetherProvisioning(type, config, receiver); } /** @@ -389,17 +395,20 @@ public class EntitlementManager { * @param receiver to receive entitlement check result. */ @VisibleForTesting - protected void runUiTetherProvisioning(int type, int subId, ResultReceiver receiver) { + protected Intent runUiTetherProvisioning(int type, final TetheringConfiguration config, + ResultReceiver receiver) { if (DBG) mLog.i("runUiTetherProvisioning: " + type); Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING_UI); intent.putExtra(EXTRA_ADD_TETHER_TYPE, type); + intent.putExtra(EXTRA_TETHER_UI_PROVISIONING_APP_NAME, config.provisioningApp); intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver); - intent.putExtra(EXTRA_SUBID, subId); + intent.putExtra(EXTRA_TETHER_SUBID, config.activeDataSubId); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // Only launch entitlement UI for system user. Entitlement UI should not appear for other // user because only admin user is allowed to change tethering. mContext.startActivity(intent); + return intent; } // Not needed to check if this don't run on the handler thread because it's private. @@ -631,7 +640,7 @@ public class EntitlementManager { receiver.send(cacheValue, null); } else { ResultReceiver proxy = buildProxyReceiver(downstream, false/* notifyFail */, receiver); - runUiTetherProvisioning(downstream, config.activeDataSubId, proxy); + runUiTetherProvisioning(downstream, config, proxy); } } } diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java index 48a600dfe6..1d45f129b5 100644 --- a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java @@ -107,6 +107,7 @@ public class TetheringConfiguration { public final String[] provisioningApp; public final String provisioningAppNoUi; public final int provisioningCheckPeriod; + public final String provisioningResponse; public final int activeDataSubId; @@ -141,10 +142,13 @@ public class TetheringConfiguration { enableLegacyDhcpServer = getEnableLegacyDhcpServer(res); provisioningApp = getResourceStringArray(res, R.array.config_mobile_hotspot_provision_app); - provisioningAppNoUi = getProvisioningAppNoUi(res); + provisioningAppNoUi = getResourceString(res, + R.string.config_mobile_hotspot_provision_app_no_ui); provisioningCheckPeriod = getResourceInteger(res, R.integer.config_mobile_hotspot_provision_check_period, 0 /* No periodic re-check */); + provisioningResponse = getResourceString(res, + R.string.config_mobile_hotspot_provision_response); mOffloadPollInterval = getResourceInteger(res, R.integer.config_tether_offload_poll_interval, @@ -337,9 +341,9 @@ public class TetheringConfiguration { return copy(LEGACY_DHCP_DEFAULT_RANGE); } - private static String getProvisioningAppNoUi(Resources res) { + private static String getResourceString(Resources res, final int resId) { try { - return res.getString(R.string.config_mobile_hotspot_provision_app_no_ui); + return res.getString(resId); } catch (Resources.NotFoundException e) { return ""; } diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java index 72fa916b9e..354e75356e 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java @@ -16,8 +16,16 @@ package com.android.networkstack.tethering; +import static android.net.TetheringConstants.EXTRA_ADD_TETHER_TYPE; +import static android.net.TetheringConstants.EXTRA_PROVISION_CALLBACK; +import static android.net.TetheringConstants.EXTRA_RUN_PROVISION; +import static android.net.TetheringConstants.EXTRA_TETHER_PROVISIONING_RESPONSE; +import static android.net.TetheringConstants.EXTRA_TETHER_SILENT_PROVISIONING_ACTION; +import static android.net.TetheringConstants.EXTRA_TETHER_SUBID; +import static android.net.TetheringConstants.EXTRA_TETHER_UI_PROVISIONING_APP_NAME; import static android.net.TetheringManager.TETHERING_BLUETOOTH; import static android.net.TetheringManager.TETHERING_ETHERNET; +import static android.net.TetheringManager.TETHERING_INVALID; import static android.net.TetheringManager.TETHERING_USB; import static android.net.TetheringManager.TETHERING_WIFI; import static android.net.TetheringManager.TETHERING_WIFI_P2P; @@ -44,6 +52,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; +import android.content.Intent; import android.content.res.Resources; import android.net.util.SharedLog; import android.os.Bundle; @@ -53,6 +62,7 @@ import android.os.ResultReceiver; import android.os.SystemProperties; import android.os.test.TestLooper; import android.provider.DeviceConfig; +import android.provider.Settings; import android.telephony.CarrierConfigManager; import androidx.test.filters.SmallTest; @@ -76,6 +86,7 @@ public final class EntitlementManagerTest { private static final String[] PROVISIONING_APP_NAME = {"some", "app"}; private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app"; + private static final String PROVISIONING_APP_RESPONSE = "app_response"; @Mock private CarrierConfigManager mCarrierConfigManager; @Mock private Context mContext; @@ -122,15 +133,51 @@ public final class EntitlementManagerTest { } @Override - protected void runUiTetherProvisioning(int type, int subId, ResultReceiver receiver) { + protected Intent runUiTetherProvisioning(int type, + final TetheringConfiguration config, final ResultReceiver receiver) { + Intent intent = super.runUiTetherProvisioning(type, config, receiver); + assertUiTetherProvisioningIntent(type, config, receiver, intent); uiProvisionCount++; receiver.send(fakeEntitlementResult, null); + return intent; + } + + private void assertUiTetherProvisioningIntent(int type, final TetheringConfiguration config, + final ResultReceiver receiver, final Intent intent) { + assertEquals(Settings.ACTION_TETHER_PROVISIONING_UI, intent.getAction()); + assertEquals(type, intent.getIntExtra(EXTRA_ADD_TETHER_TYPE, TETHERING_INVALID)); + final String[] appName = intent.getStringArrayExtra( + EXTRA_TETHER_UI_PROVISIONING_APP_NAME); + assertEquals(PROVISIONING_APP_NAME.length, appName.length); + for (int i = 0; i < PROVISIONING_APP_NAME.length; i++) { + assertEquals(PROVISIONING_APP_NAME[i], appName[i]); + } + assertEquals(receiver, intent.getParcelableExtra(EXTRA_PROVISION_CALLBACK)); + assertEquals(config.activeDataSubId, + intent.getIntExtra(EXTRA_TETHER_SUBID, INVALID_SUBSCRIPTION_ID)); } @Override - protected void runSilentTetherProvisioning(int type, int subId) { + protected Intent runSilentTetherProvisioning(int type, + final TetheringConfiguration config) { + Intent intent = super.runSilentTetherProvisioning(type, config); + assertSilentTetherProvisioning(type, config, intent); silentProvisionCount++; addDownstreamMapping(type, fakeEntitlementResult); + return intent; + } + + private void assertSilentTetherProvisioning(int type, final TetheringConfiguration config, + final Intent intent) { + assertEquals(type, intent.getIntExtra(EXTRA_ADD_TETHER_TYPE, TETHERING_INVALID)); + assertEquals(true, intent.getBooleanExtra(EXTRA_RUN_PROVISION, false)); + assertEquals(PROVISIONING_NO_UI_APP_NAME, + intent.getStringExtra(EXTRA_TETHER_SILENT_PROVISIONING_ACTION)); + assertEquals(PROVISIONING_APP_RESPONSE, + intent.getStringExtra(EXTRA_TETHER_PROVISIONING_RESPONSE)); + assertTrue(intent.hasExtra(EXTRA_PROVISION_CALLBACK)); + assertEquals(config.activeDataSubId, + intent.getIntExtra(EXTRA_TETHER_SUBID, INVALID_SUBSCRIPTION_ID)); } } @@ -187,6 +234,8 @@ public final class EntitlementManagerTest { .thenReturn(PROVISIONING_APP_NAME); when(mResources.getString(R.string.config_mobile_hotspot_provision_app_no_ui)) .thenReturn(PROVISIONING_NO_UI_APP_NAME); + when(mResources.getString(R.string.config_mobile_hotspot_provision_response)).thenReturn( + PROVISIONING_APP_RESPONSE); // Act like the CarrierConfigManager is present and ready unless told otherwise. when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE)) .thenReturn(mCarrierConfigManager); diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java index 1999ad786e..312186391d 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java @@ -61,6 +61,8 @@ public class TetheringConfigurationTest { private final SharedLog mLog = new SharedLog("TetheringConfigurationTest"); private static final String[] PROVISIONING_APP_NAME = {"some", "app"}; + private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app"; + private static final String PROVISIONING_APP_RESPONSE = "app_response"; @Mock private Context mContext; @Mock private TelephonyManager mTelephonyManager; @Mock private Resources mResources; @@ -388,6 +390,8 @@ public class TetheringConfigurationTest { new MockTetheringConfiguration(mMockContext, mLog, anyValidSubId); assertEquals(mockCfg.provisioningApp[0], PROVISIONING_APP_NAME[0]); assertEquals(mockCfg.provisioningApp[1], PROVISIONING_APP_NAME[1]); + assertEquals(mockCfg.provisioningAppNoUi, PROVISIONING_NO_UI_APP_NAME); + assertEquals(mockCfg.provisioningResponse, PROVISIONING_APP_RESPONSE); } private void setUpResourceForSubId() { @@ -403,6 +407,10 @@ public class TetheringConfigurationTest { new int[0]); when(mResourcesForSubId.getStringArray( R.array.config_mobile_hotspot_provision_app)).thenReturn(PROVISIONING_APP_NAME); + when(mResourcesForSubId.getString(R.string.config_mobile_hotspot_provision_app_no_ui)) + .thenReturn(PROVISIONING_NO_UI_APP_NAME); + when(mResourcesForSubId.getString( + R.string.config_mobile_hotspot_provision_response)).thenReturn( + PROVISIONING_APP_RESPONSE); } - } From e10b80de7d642283771305c4cb7dac9eeabf647e Mon Sep 17 00:00:00 2001 From: Remi NGUYEN VAN Date: Mon, 8 Jun 2020 15:40:06 +0900 Subject: [PATCH 165/188] Move Inet[4]AddressUtils to libs/net The classes should not be picked up from frameworks/base, as they are part of several mainline modules. Also refine comments in DhcpResults following feedback in previous change. Bug: 151052811 Test: m; manual: flashed, wifi and telephony working Test: atest NetworkStackCoverageTests Change-Id: I7074651c6a2a7a6b11bcf13cc4bb03833d7d655f --- Tethering/jarjar-rules.txt | 18 ++++++------------ .../net/dhcp/DhcpServingParamsParcelExt.java | 2 +- Tethering/src/android/net/ip/IpServer.java | 3 ++- .../unit/src/android/net/ip/IpServerTest.java | 3 ++- .../networkstack/tethering/TetheringTest.java | 2 +- 5 files changed, 12 insertions(+), 16 deletions(-) diff --git a/Tethering/jarjar-rules.txt b/Tethering/jarjar-rules.txt index e90a2ccaa2..2d3108a0bf 100644 --- a/Tethering/jarjar-rules.txt +++ b/Tethering/jarjar-rules.txt @@ -1,17 +1,11 @@ # These must be kept in sync with the framework-tethering-shared-srcs filegroup. -# If there are files in that filegroup that do not appear here, the classes in the +# Classes from the framework-tethering-shared-srcs filegroup. +# If there are files in that filegroup that are not covered below, the classes in the # module will be overwritten by the ones in the framework. -# Don't jar-jar the entire package because tethering still use some internal classes -# (like TrafficStatsConstants in com.android.internal.util) -# TODO: simply these when tethering is built as system_current. -rule com.android.internal.util.BitUtils* com.android.networkstack.tethering.util.BitUtils@1 -rule com.android.internal.util.IndentingPrintWriter.java* com.android.networkstack.tethering.util.IndentingPrintWriter.java@1 -rule com.android.internal.util.IState.java* com.android.networkstack.tethering.util.IState.java@1 -rule com.android.internal.util.MessageUtils* com.android.networkstack.tethering.util.MessageUtils@1 -rule com.android.internal.util.State* com.android.networkstack.tethering.util.State@1 -rule com.android.internal.util.StateMachine* com.android.networkstack.tethering.util.StateMachine@1 -rule com.android.internal.util.TrafficStatsConstants* com.android.networkstack.tethering.util.TrafficStatsConstants@1 - +rule com.android.internal.util.** com.android.networkstack.tethering.util.@1 rule android.net.LocalLog* com.android.networkstack.tethering.LocalLog@1 rule android.net.shared.Inet4AddressUtils* com.android.networkstack.tethering.shared.Inet4AddressUtils@1 + +# Classes from net-utils-framework-common +rule com.android.net.module.util.** com.android.networkstack.tethering.util.@1 \ No newline at end of file diff --git a/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java b/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java index 4710a30b29..aaaec17bf9 100644 --- a/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java +++ b/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java @@ -16,7 +16,7 @@ package android.net.dhcp; -import static android.net.shared.Inet4AddressUtils.inet4AddressToIntHTH; +import static com.android.net.module.util.Inet4AddressUtils.inet4AddressToIntHTH; import android.net.LinkAddress; import android.util.ArraySet; diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java index 1671dda4bd..5239947403 100644 --- a/Tethering/src/android/net/ip/IpServer.java +++ b/Tethering/src/android/net/ip/IpServer.java @@ -19,13 +19,14 @@ package android.net.ip; import static android.net.RouteInfo.RTN_UNICAST; import static android.net.TetheringManager.TetheringRequest.checkStaticAddressConfiguration; import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS; -import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH; import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH; import static android.net.util.NetworkConstants.asByte; import static android.net.util.PrefixUtils.asIpPrefix; import static android.net.util.TetheringMessageBase.BASE_IPSERVER; import static android.system.OsConstants.RT_SCOPE_UNIVERSE; +import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH; + import android.net.INetd; import android.net.INetworkStackStatusCallback; import android.net.IpPrefix; diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java index c3bc915a23..2035744173 100644 --- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java +++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java @@ -36,7 +36,8 @@ import static android.net.netlink.NetlinkConstants.RTM_NEWNEIGH; import static android.net.netlink.StructNdMsg.NUD_FAILED; import static android.net.netlink.StructNdMsg.NUD_REACHABLE; import static android.net.netlink.StructNdMsg.NUD_STALE; -import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH; + +import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; 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 8146a58ddd..1d6fdea6be 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java @@ -39,7 +39,6 @@ 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.dhcp.IDhcpServer.STATUS_SUCCESS; -import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE; @@ -48,6 +47,7 @@ import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED; import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; +import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH; import static com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE; import static com.android.networkstack.tethering.UpstreamNetworkMonitor.EVENT_ON_CAPABILITIES; From 99889031746a0863b2eb88ec33dc954afab64762 Mon Sep 17 00:00:00 2001 From: Mark Chien Date: Fri, 12 Jun 2020 15:28:22 +0000 Subject: [PATCH 166/188] Revert "Pass entitlement configuration to Settings for entitlement check" Original CL has dependencies with unmerged settings change: https://googleplex-android-review.git.corp.google.com/c/platform/packages/apps/Settings/+/11524847 They should be in the same topic, revert it first. Will resume it and put the same with settings part CL. This reverts commit 217d7b01f87dcc960572bb88e7ec544d5ce1dc43. Reason for revert: This break hotspot because it should merged with settings part together. Bug: 158836492 Change-Id: I94d3ee25168cfb3d125030654c4bb8ddd670abfc --- .../src/android/net/TetheringConstants.java | 38 ++----------- .../tethering/EntitlementManager.java | 33 +++++------- .../tethering/TetheringConfiguration.java | 10 ++-- .../tethering/EntitlementManagerTest.java | 53 +------------------ .../tethering/TetheringConfigurationTest.java | 10 +--- 5 files changed, 22 insertions(+), 122 deletions(-) diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java b/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java index f14def6a3a..fd6f171487 100644 --- a/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java +++ b/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java @@ -37,8 +37,8 @@ public final class TetheringConstants { private TetheringConstants() { } /** - * Extra used for communicating with the TetherService and TetherProvisioningActivity. - * Includes the type of tethering to enable if any. + * Extra used for communicating with the TetherService. Includes the type of tethering to + * enable if any. */ public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType"; /** @@ -56,38 +56,8 @@ public final class TetheringConstants { */ public static final String EXTRA_RUN_PROVISION = "extraRunProvision"; /** - * Extra used for communicating with the TetherService and TetherProvisioningActivity. - * Contains the {@link ResultReceiver} which will receive provisioning results. - * Can not be empty. + * Extra used for communicating with the TetherService. Contains the {@link ResultReceiver} + * which will receive provisioning results. Can be left empty. */ public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback"; - - /** - * Extra used for communicating with the TetherService and TetherProvisioningActivity. - * Contains the subId of current active cellular upstream. - * @hide - */ - public static final String EXTRA_TETHER_SUBID = "android.net.extra.TETHER_SUBID"; - - /** - * Extra used for telling TetherProvisioningActivity the entitlement package name and class - * name to start UI entitlement check. - * @hide - */ - public static final String EXTRA_TETHER_UI_PROVISIONING_APP_NAME = - "android.net.extra.TETHER_UI_PROVISIONING_APP_NAME"; - - /** - * Extra used for telling TetherService the intent action to start silent entitlement check. - * @hide - */ - public static final String EXTRA_TETHER_SILENT_PROVISIONING_ACTION = - "android.net.extra.TETHER_SILENT_PROVISIONING_ACTION"; - - /** - * Extra used for TetherService to receive the response of provisioning check. - * @hide - */ - public static final String EXTRA_TETHER_PROVISIONING_RESPONSE = - "android.net.extra.TETHER_PROVISIONING_RESPONSE"; } diff --git a/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java b/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java index 9dace709d7..3c6e8d88ed 100644 --- a/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java +++ b/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java @@ -19,10 +19,6 @@ package com.android.networkstack.tethering; import static android.net.TetheringConstants.EXTRA_ADD_TETHER_TYPE; import static android.net.TetheringConstants.EXTRA_PROVISION_CALLBACK; import static android.net.TetheringConstants.EXTRA_RUN_PROVISION; -import static android.net.TetheringConstants.EXTRA_TETHER_PROVISIONING_RESPONSE; -import static android.net.TetheringConstants.EXTRA_TETHER_SILENT_PROVISIONING_ACTION; -import static android.net.TetheringConstants.EXTRA_TETHER_SUBID; -import static android.net.TetheringConstants.EXTRA_TETHER_UI_PROVISIONING_APP_NAME; import static android.net.TetheringManager.TETHERING_BLUETOOTH; import static android.net.TetheringManager.TETHERING_ETHERNET; import static android.net.TetheringManager.TETHERING_INVALID; @@ -73,6 +69,7 @@ public class EntitlementManager { protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning"; private static final String ACTION_PROVISIONING_ALARM = "com.android.networkstack.tethering.PROVISIONING_RECHECK_ALARM"; + private static final String EXTRA_SUBID = "subId"; private final ComponentName mSilentProvisioningService; private static final int MS_PER_HOUR = 60 * 60 * 1000; @@ -200,9 +197,9 @@ public class EntitlementManager { // till upstream change to cellular. if (mUsingCellularAsUpstream) { if (showProvisioningUi) { - runUiTetherProvisioning(downstreamType, config); + runUiTetherProvisioning(downstreamType, config.activeDataSubId); } else { - runSilentTetherProvisioning(downstreamType, config); + runSilentTetherProvisioning(downstreamType, config.activeDataSubId); } mNeedReRunProvisioningUi = false; } else { @@ -265,9 +262,9 @@ public class EntitlementManager { if (mCurrentEntitlementResults.indexOfKey(downstream) < 0) { if (mNeedReRunProvisioningUi) { mNeedReRunProvisioningUi = false; - runUiTetherProvisioning(downstream, config); + runUiTetherProvisioning(downstream, config.activeDataSubId); } else { - runSilentTetherProvisioning(downstream, config); + runSilentTetherProvisioning(downstream, config.activeDataSubId); } } } @@ -364,7 +361,7 @@ public class EntitlementManager { * @param subId default data subscription ID. */ @VisibleForTesting - protected Intent runSilentTetherProvisioning(int type, final TetheringConfiguration config) { + protected void runSilentTetherProvisioning(int type, int subId) { if (DBG) mLog.i("runSilentTetherProvisioning: " + type); // For silent provisioning, settings would stop tethering when entitlement fail. ResultReceiver receiver = buildProxyReceiver(type, false/* notifyFail */, null); @@ -372,20 +369,17 @@ public class EntitlementManager { Intent intent = new Intent(); intent.putExtra(EXTRA_ADD_TETHER_TYPE, type); intent.putExtra(EXTRA_RUN_PROVISION, true); - intent.putExtra(EXTRA_TETHER_SILENT_PROVISIONING_ACTION, config.provisioningAppNoUi); - intent.putExtra(EXTRA_TETHER_PROVISIONING_RESPONSE, config.provisioningResponse); intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver); - intent.putExtra(EXTRA_TETHER_SUBID, config.activeDataSubId); + intent.putExtra(EXTRA_SUBID, subId); intent.setComponent(mSilentProvisioningService); // Only admin user can change tethering and SilentTetherProvisioning don't need to // show UI, it is fine to always start setting's background service as system user. mContext.startService(intent); - return intent; } - private void runUiTetherProvisioning(int type, final TetheringConfiguration config) { + private void runUiTetherProvisioning(int type, int subId) { ResultReceiver receiver = buildProxyReceiver(type, true/* notifyFail */, null); - runUiTetherProvisioning(type, config, receiver); + runUiTetherProvisioning(type, subId, receiver); } /** @@ -395,20 +389,17 @@ public class EntitlementManager { * @param receiver to receive entitlement check result. */ @VisibleForTesting - protected Intent runUiTetherProvisioning(int type, final TetheringConfiguration config, - ResultReceiver receiver) { + protected void runUiTetherProvisioning(int type, int subId, ResultReceiver receiver) { if (DBG) mLog.i("runUiTetherProvisioning: " + type); Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING_UI); intent.putExtra(EXTRA_ADD_TETHER_TYPE, type); - intent.putExtra(EXTRA_TETHER_UI_PROVISIONING_APP_NAME, config.provisioningApp); intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver); - intent.putExtra(EXTRA_TETHER_SUBID, config.activeDataSubId); + intent.putExtra(EXTRA_SUBID, subId); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // Only launch entitlement UI for system user. Entitlement UI should not appear for other // user because only admin user is allowed to change tethering. mContext.startActivity(intent); - return intent; } // Not needed to check if this don't run on the handler thread because it's private. @@ -640,7 +631,7 @@ public class EntitlementManager { receiver.send(cacheValue, null); } else { ResultReceiver proxy = buildProxyReceiver(downstream, false/* notifyFail */, receiver); - runUiTetherProvisioning(downstream, config, proxy); + runUiTetherProvisioning(downstream, config.activeDataSubId, proxy); } } } diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java index 1d45f129b5..48a600dfe6 100644 --- a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java @@ -107,7 +107,6 @@ public class TetheringConfiguration { public final String[] provisioningApp; public final String provisioningAppNoUi; public final int provisioningCheckPeriod; - public final String provisioningResponse; public final int activeDataSubId; @@ -142,13 +141,10 @@ public class TetheringConfiguration { enableLegacyDhcpServer = getEnableLegacyDhcpServer(res); provisioningApp = getResourceStringArray(res, R.array.config_mobile_hotspot_provision_app); - provisioningAppNoUi = getResourceString(res, - R.string.config_mobile_hotspot_provision_app_no_ui); + provisioningAppNoUi = getProvisioningAppNoUi(res); provisioningCheckPeriod = getResourceInteger(res, R.integer.config_mobile_hotspot_provision_check_period, 0 /* No periodic re-check */); - provisioningResponse = getResourceString(res, - R.string.config_mobile_hotspot_provision_response); mOffloadPollInterval = getResourceInteger(res, R.integer.config_tether_offload_poll_interval, @@ -341,9 +337,9 @@ public class TetheringConfiguration { return copy(LEGACY_DHCP_DEFAULT_RANGE); } - private static String getResourceString(Resources res, final int resId) { + private static String getProvisioningAppNoUi(Resources res) { try { - return res.getString(resId); + return res.getString(R.string.config_mobile_hotspot_provision_app_no_ui); } catch (Resources.NotFoundException e) { return ""; } diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java index 354e75356e..72fa916b9e 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java @@ -16,16 +16,8 @@ package com.android.networkstack.tethering; -import static android.net.TetheringConstants.EXTRA_ADD_TETHER_TYPE; -import static android.net.TetheringConstants.EXTRA_PROVISION_CALLBACK; -import static android.net.TetheringConstants.EXTRA_RUN_PROVISION; -import static android.net.TetheringConstants.EXTRA_TETHER_PROVISIONING_RESPONSE; -import static android.net.TetheringConstants.EXTRA_TETHER_SILENT_PROVISIONING_ACTION; -import static android.net.TetheringConstants.EXTRA_TETHER_SUBID; -import static android.net.TetheringConstants.EXTRA_TETHER_UI_PROVISIONING_APP_NAME; import static android.net.TetheringManager.TETHERING_BLUETOOTH; import static android.net.TetheringManager.TETHERING_ETHERNET; -import static android.net.TetheringManager.TETHERING_INVALID; import static android.net.TetheringManager.TETHERING_USB; import static android.net.TetheringManager.TETHERING_WIFI; import static android.net.TetheringManager.TETHERING_WIFI_P2P; @@ -52,7 +44,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; -import android.content.Intent; import android.content.res.Resources; import android.net.util.SharedLog; import android.os.Bundle; @@ -62,7 +53,6 @@ import android.os.ResultReceiver; import android.os.SystemProperties; import android.os.test.TestLooper; import android.provider.DeviceConfig; -import android.provider.Settings; import android.telephony.CarrierConfigManager; import androidx.test.filters.SmallTest; @@ -86,7 +76,6 @@ public final class EntitlementManagerTest { private static final String[] PROVISIONING_APP_NAME = {"some", "app"}; private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app"; - private static final String PROVISIONING_APP_RESPONSE = "app_response"; @Mock private CarrierConfigManager mCarrierConfigManager; @Mock private Context mContext; @@ -133,51 +122,15 @@ public final class EntitlementManagerTest { } @Override - protected Intent runUiTetherProvisioning(int type, - final TetheringConfiguration config, final ResultReceiver receiver) { - Intent intent = super.runUiTetherProvisioning(type, config, receiver); - assertUiTetherProvisioningIntent(type, config, receiver, intent); + protected void runUiTetherProvisioning(int type, int subId, ResultReceiver receiver) { uiProvisionCount++; receiver.send(fakeEntitlementResult, null); - return intent; - } - - private void assertUiTetherProvisioningIntent(int type, final TetheringConfiguration config, - final ResultReceiver receiver, final Intent intent) { - assertEquals(Settings.ACTION_TETHER_PROVISIONING_UI, intent.getAction()); - assertEquals(type, intent.getIntExtra(EXTRA_ADD_TETHER_TYPE, TETHERING_INVALID)); - final String[] appName = intent.getStringArrayExtra( - EXTRA_TETHER_UI_PROVISIONING_APP_NAME); - assertEquals(PROVISIONING_APP_NAME.length, appName.length); - for (int i = 0; i < PROVISIONING_APP_NAME.length; i++) { - assertEquals(PROVISIONING_APP_NAME[i], appName[i]); - } - assertEquals(receiver, intent.getParcelableExtra(EXTRA_PROVISION_CALLBACK)); - assertEquals(config.activeDataSubId, - intent.getIntExtra(EXTRA_TETHER_SUBID, INVALID_SUBSCRIPTION_ID)); } @Override - protected Intent runSilentTetherProvisioning(int type, - final TetheringConfiguration config) { - Intent intent = super.runSilentTetherProvisioning(type, config); - assertSilentTetherProvisioning(type, config, intent); + protected void runSilentTetherProvisioning(int type, int subId) { silentProvisionCount++; addDownstreamMapping(type, fakeEntitlementResult); - return intent; - } - - private void assertSilentTetherProvisioning(int type, final TetheringConfiguration config, - final Intent intent) { - assertEquals(type, intent.getIntExtra(EXTRA_ADD_TETHER_TYPE, TETHERING_INVALID)); - assertEquals(true, intent.getBooleanExtra(EXTRA_RUN_PROVISION, false)); - assertEquals(PROVISIONING_NO_UI_APP_NAME, - intent.getStringExtra(EXTRA_TETHER_SILENT_PROVISIONING_ACTION)); - assertEquals(PROVISIONING_APP_RESPONSE, - intent.getStringExtra(EXTRA_TETHER_PROVISIONING_RESPONSE)); - assertTrue(intent.hasExtra(EXTRA_PROVISION_CALLBACK)); - assertEquals(config.activeDataSubId, - intent.getIntExtra(EXTRA_TETHER_SUBID, INVALID_SUBSCRIPTION_ID)); } } @@ -234,8 +187,6 @@ public final class EntitlementManagerTest { .thenReturn(PROVISIONING_APP_NAME); when(mResources.getString(R.string.config_mobile_hotspot_provision_app_no_ui)) .thenReturn(PROVISIONING_NO_UI_APP_NAME); - when(mResources.getString(R.string.config_mobile_hotspot_provision_response)).thenReturn( - PROVISIONING_APP_RESPONSE); // Act like the CarrierConfigManager is present and ready unless told otherwise. when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE)) .thenReturn(mCarrierConfigManager); diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java index 312186391d..1999ad786e 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java @@ -61,8 +61,6 @@ public class TetheringConfigurationTest { private final SharedLog mLog = new SharedLog("TetheringConfigurationTest"); private static final String[] PROVISIONING_APP_NAME = {"some", "app"}; - private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app"; - private static final String PROVISIONING_APP_RESPONSE = "app_response"; @Mock private Context mContext; @Mock private TelephonyManager mTelephonyManager; @Mock private Resources mResources; @@ -390,8 +388,6 @@ public class TetheringConfigurationTest { new MockTetheringConfiguration(mMockContext, mLog, anyValidSubId); assertEquals(mockCfg.provisioningApp[0], PROVISIONING_APP_NAME[0]); assertEquals(mockCfg.provisioningApp[1], PROVISIONING_APP_NAME[1]); - assertEquals(mockCfg.provisioningAppNoUi, PROVISIONING_NO_UI_APP_NAME); - assertEquals(mockCfg.provisioningResponse, PROVISIONING_APP_RESPONSE); } private void setUpResourceForSubId() { @@ -407,10 +403,6 @@ public class TetheringConfigurationTest { new int[0]); when(mResourcesForSubId.getStringArray( R.array.config_mobile_hotspot_provision_app)).thenReturn(PROVISIONING_APP_NAME); - when(mResourcesForSubId.getString(R.string.config_mobile_hotspot_provision_app_no_ui)) - .thenReturn(PROVISIONING_NO_UI_APP_NAME); - when(mResourcesForSubId.getString( - R.string.config_mobile_hotspot_provision_response)).thenReturn( - PROVISIONING_APP_RESPONSE); } + } From 0d0ea27274afe400644754f2505d3fb18ec43416 Mon Sep 17 00:00:00 2001 From: Remi NGUYEN VAN Date: Mon, 15 Jun 2020 08:03:02 +0000 Subject: [PATCH 167/188] Use unstable networkstack AIDLs in development branches Development branches should use the -unstable version of the NetworkStack AIDLs so that refreezing is not necessary for each modification. The versions will be re-frozen before each release instead. Bug: 157534516 Change-Id: I74b4a16266bda7b8ac740b3a0193268da260fc2f Test: m --- Tethering/Android.bp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Tethering/Android.bp b/Tethering/Android.bp index 9eba6bd0dc..9c8c0aab2d 100644 --- a/Tethering/Android.bp +++ b/Tethering/Android.bp @@ -27,7 +27,8 @@ java_defaults { "androidx.annotation_annotation", "netd_aidl_interface-unstable-java", "netlink-client", - "networkstack-aidl-interfaces-java", + // TODO: use networkstack-client instead of just including the AIDL interface + "networkstack-aidl-interfaces-unstable-java", "android.hardware.tetheroffload.config-V1.0-java", "android.hardware.tetheroffload.control-V1.0-java", "net-utils-framework-common", From c55e229dcb3dd5cedc8ffbf29993cadafe29392a Mon Sep 17 00:00:00 2001 From: Mark Chien Date: Fri, 12 Jun 2020 17:35:18 +0000 Subject: [PATCH 168/188] Revert "Revert "Pass entitlement configuration to Settings for entitlement check"" Add commit message here for reference: Tethering resource configuration is move from framework to tethering module. The resource would not be accessible from outside of tethering module. List the replacements of framework resources usage and intent extra: 1. R.string.config_mobile_hotspot_provision_response --> android.net.extra.TETHER_PROVISIONING_RESPONSE. 2. R.string.config_mobile_hotspot_provision_app_no_ui --> android.net.extra.TETHER_UI_PROVISIONING_APP_NAME 3. R.array.config_mobile_hotspot_provision_app --> android.net.extra.TETHER_SILENT_PROVISIONING_ACTION Besides, the current active subId would put in android.net.extra.TETHER_SUBID Note: They are not APIs because of API freeze. Now both tethering module and Settings define these strings independently. Bug: 146918263 Test: atest TetherServiceTest atest TetherProvisioningActivityTest This reverts commit 99889031746a0863b2eb88ec33dc954afab64762. Reason for revert: Resume the CL and put this CL with settings part in the same topic to avoid break. Change-Id: I114b4c258743661df51e5a969e150047a292e035 --- .../src/android/net/TetheringConstants.java | 38 +++++++++++-- .../tethering/EntitlementManager.java | 33 +++++++----- .../tethering/TetheringConfiguration.java | 10 ++-- .../tethering/EntitlementManagerTest.java | 53 ++++++++++++++++++- .../tethering/TetheringConfigurationTest.java | 10 +++- 5 files changed, 122 insertions(+), 22 deletions(-) diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java b/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java index fd6f171487..f14def6a3a 100644 --- a/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java +++ b/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java @@ -37,8 +37,8 @@ public final class TetheringConstants { private TetheringConstants() { } /** - * Extra used for communicating with the TetherService. Includes the type of tethering to - * enable if any. + * Extra used for communicating with the TetherService and TetherProvisioningActivity. + * Includes the type of tethering to enable if any. */ public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType"; /** @@ -56,8 +56,38 @@ public final class TetheringConstants { */ public static final String EXTRA_RUN_PROVISION = "extraRunProvision"; /** - * Extra used for communicating with the TetherService. Contains the {@link ResultReceiver} - * which will receive provisioning results. Can be left empty. + * Extra used for communicating with the TetherService and TetherProvisioningActivity. + * Contains the {@link ResultReceiver} which will receive provisioning results. + * Can not be empty. */ public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback"; + + /** + * Extra used for communicating with the TetherService and TetherProvisioningActivity. + * Contains the subId of current active cellular upstream. + * @hide + */ + public static final String EXTRA_TETHER_SUBID = "android.net.extra.TETHER_SUBID"; + + /** + * Extra used for telling TetherProvisioningActivity the entitlement package name and class + * name to start UI entitlement check. + * @hide + */ + public static final String EXTRA_TETHER_UI_PROVISIONING_APP_NAME = + "android.net.extra.TETHER_UI_PROVISIONING_APP_NAME"; + + /** + * Extra used for telling TetherService the intent action to start silent entitlement check. + * @hide + */ + public static final String EXTRA_TETHER_SILENT_PROVISIONING_ACTION = + "android.net.extra.TETHER_SILENT_PROVISIONING_ACTION"; + + /** + * Extra used for TetherService to receive the response of provisioning check. + * @hide + */ + public static final String EXTRA_TETHER_PROVISIONING_RESPONSE = + "android.net.extra.TETHER_PROVISIONING_RESPONSE"; } diff --git a/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java b/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java index 3c6e8d88ed..9dace709d7 100644 --- a/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java +++ b/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java @@ -19,6 +19,10 @@ package com.android.networkstack.tethering; import static android.net.TetheringConstants.EXTRA_ADD_TETHER_TYPE; import static android.net.TetheringConstants.EXTRA_PROVISION_CALLBACK; import static android.net.TetheringConstants.EXTRA_RUN_PROVISION; +import static android.net.TetheringConstants.EXTRA_TETHER_PROVISIONING_RESPONSE; +import static android.net.TetheringConstants.EXTRA_TETHER_SILENT_PROVISIONING_ACTION; +import static android.net.TetheringConstants.EXTRA_TETHER_SUBID; +import static android.net.TetheringConstants.EXTRA_TETHER_UI_PROVISIONING_APP_NAME; import static android.net.TetheringManager.TETHERING_BLUETOOTH; import static android.net.TetheringManager.TETHERING_ETHERNET; import static android.net.TetheringManager.TETHERING_INVALID; @@ -69,7 +73,6 @@ public class EntitlementManager { protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning"; private static final String ACTION_PROVISIONING_ALARM = "com.android.networkstack.tethering.PROVISIONING_RECHECK_ALARM"; - private static final String EXTRA_SUBID = "subId"; private final ComponentName mSilentProvisioningService; private static final int MS_PER_HOUR = 60 * 60 * 1000; @@ -197,9 +200,9 @@ public class EntitlementManager { // till upstream change to cellular. if (mUsingCellularAsUpstream) { if (showProvisioningUi) { - runUiTetherProvisioning(downstreamType, config.activeDataSubId); + runUiTetherProvisioning(downstreamType, config); } else { - runSilentTetherProvisioning(downstreamType, config.activeDataSubId); + runSilentTetherProvisioning(downstreamType, config); } mNeedReRunProvisioningUi = false; } else { @@ -262,9 +265,9 @@ public class EntitlementManager { if (mCurrentEntitlementResults.indexOfKey(downstream) < 0) { if (mNeedReRunProvisioningUi) { mNeedReRunProvisioningUi = false; - runUiTetherProvisioning(downstream, config.activeDataSubId); + runUiTetherProvisioning(downstream, config); } else { - runSilentTetherProvisioning(downstream, config.activeDataSubId); + runSilentTetherProvisioning(downstream, config); } } } @@ -361,7 +364,7 @@ public class EntitlementManager { * @param subId default data subscription ID. */ @VisibleForTesting - protected void runSilentTetherProvisioning(int type, int subId) { + protected Intent runSilentTetherProvisioning(int type, final TetheringConfiguration config) { if (DBG) mLog.i("runSilentTetherProvisioning: " + type); // For silent provisioning, settings would stop tethering when entitlement fail. ResultReceiver receiver = buildProxyReceiver(type, false/* notifyFail */, null); @@ -369,17 +372,20 @@ public class EntitlementManager { Intent intent = new Intent(); intent.putExtra(EXTRA_ADD_TETHER_TYPE, type); intent.putExtra(EXTRA_RUN_PROVISION, true); + intent.putExtra(EXTRA_TETHER_SILENT_PROVISIONING_ACTION, config.provisioningAppNoUi); + intent.putExtra(EXTRA_TETHER_PROVISIONING_RESPONSE, config.provisioningResponse); intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver); - intent.putExtra(EXTRA_SUBID, subId); + intent.putExtra(EXTRA_TETHER_SUBID, config.activeDataSubId); intent.setComponent(mSilentProvisioningService); // Only admin user can change tethering and SilentTetherProvisioning don't need to // show UI, it is fine to always start setting's background service as system user. mContext.startService(intent); + return intent; } - private void runUiTetherProvisioning(int type, int subId) { + private void runUiTetherProvisioning(int type, final TetheringConfiguration config) { ResultReceiver receiver = buildProxyReceiver(type, true/* notifyFail */, null); - runUiTetherProvisioning(type, subId, receiver); + runUiTetherProvisioning(type, config, receiver); } /** @@ -389,17 +395,20 @@ public class EntitlementManager { * @param receiver to receive entitlement check result. */ @VisibleForTesting - protected void runUiTetherProvisioning(int type, int subId, ResultReceiver receiver) { + protected Intent runUiTetherProvisioning(int type, final TetheringConfiguration config, + ResultReceiver receiver) { if (DBG) mLog.i("runUiTetherProvisioning: " + type); Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING_UI); intent.putExtra(EXTRA_ADD_TETHER_TYPE, type); + intent.putExtra(EXTRA_TETHER_UI_PROVISIONING_APP_NAME, config.provisioningApp); intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver); - intent.putExtra(EXTRA_SUBID, subId); + intent.putExtra(EXTRA_TETHER_SUBID, config.activeDataSubId); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // Only launch entitlement UI for system user. Entitlement UI should not appear for other // user because only admin user is allowed to change tethering. mContext.startActivity(intent); + return intent; } // Not needed to check if this don't run on the handler thread because it's private. @@ -631,7 +640,7 @@ public class EntitlementManager { receiver.send(cacheValue, null); } else { ResultReceiver proxy = buildProxyReceiver(downstream, false/* notifyFail */, receiver); - runUiTetherProvisioning(downstream, config.activeDataSubId, proxy); + runUiTetherProvisioning(downstream, config, proxy); } } } diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java index 48a600dfe6..1d45f129b5 100644 --- a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java @@ -107,6 +107,7 @@ public class TetheringConfiguration { public final String[] provisioningApp; public final String provisioningAppNoUi; public final int provisioningCheckPeriod; + public final String provisioningResponse; public final int activeDataSubId; @@ -141,10 +142,13 @@ public class TetheringConfiguration { enableLegacyDhcpServer = getEnableLegacyDhcpServer(res); provisioningApp = getResourceStringArray(res, R.array.config_mobile_hotspot_provision_app); - provisioningAppNoUi = getProvisioningAppNoUi(res); + provisioningAppNoUi = getResourceString(res, + R.string.config_mobile_hotspot_provision_app_no_ui); provisioningCheckPeriod = getResourceInteger(res, R.integer.config_mobile_hotspot_provision_check_period, 0 /* No periodic re-check */); + provisioningResponse = getResourceString(res, + R.string.config_mobile_hotspot_provision_response); mOffloadPollInterval = getResourceInteger(res, R.integer.config_tether_offload_poll_interval, @@ -337,9 +341,9 @@ public class TetheringConfiguration { return copy(LEGACY_DHCP_DEFAULT_RANGE); } - private static String getProvisioningAppNoUi(Resources res) { + private static String getResourceString(Resources res, final int resId) { try { - return res.getString(R.string.config_mobile_hotspot_provision_app_no_ui); + return res.getString(resId); } catch (Resources.NotFoundException e) { return ""; } diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java index 72fa916b9e..354e75356e 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java @@ -16,8 +16,16 @@ package com.android.networkstack.tethering; +import static android.net.TetheringConstants.EXTRA_ADD_TETHER_TYPE; +import static android.net.TetheringConstants.EXTRA_PROVISION_CALLBACK; +import static android.net.TetheringConstants.EXTRA_RUN_PROVISION; +import static android.net.TetheringConstants.EXTRA_TETHER_PROVISIONING_RESPONSE; +import static android.net.TetheringConstants.EXTRA_TETHER_SILENT_PROVISIONING_ACTION; +import static android.net.TetheringConstants.EXTRA_TETHER_SUBID; +import static android.net.TetheringConstants.EXTRA_TETHER_UI_PROVISIONING_APP_NAME; import static android.net.TetheringManager.TETHERING_BLUETOOTH; import static android.net.TetheringManager.TETHERING_ETHERNET; +import static android.net.TetheringManager.TETHERING_INVALID; import static android.net.TetheringManager.TETHERING_USB; import static android.net.TetheringManager.TETHERING_WIFI; import static android.net.TetheringManager.TETHERING_WIFI_P2P; @@ -44,6 +52,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; +import android.content.Intent; import android.content.res.Resources; import android.net.util.SharedLog; import android.os.Bundle; @@ -53,6 +62,7 @@ import android.os.ResultReceiver; import android.os.SystemProperties; import android.os.test.TestLooper; import android.provider.DeviceConfig; +import android.provider.Settings; import android.telephony.CarrierConfigManager; import androidx.test.filters.SmallTest; @@ -76,6 +86,7 @@ public final class EntitlementManagerTest { private static final String[] PROVISIONING_APP_NAME = {"some", "app"}; private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app"; + private static final String PROVISIONING_APP_RESPONSE = "app_response"; @Mock private CarrierConfigManager mCarrierConfigManager; @Mock private Context mContext; @@ -122,15 +133,51 @@ public final class EntitlementManagerTest { } @Override - protected void runUiTetherProvisioning(int type, int subId, ResultReceiver receiver) { + protected Intent runUiTetherProvisioning(int type, + final TetheringConfiguration config, final ResultReceiver receiver) { + Intent intent = super.runUiTetherProvisioning(type, config, receiver); + assertUiTetherProvisioningIntent(type, config, receiver, intent); uiProvisionCount++; receiver.send(fakeEntitlementResult, null); + return intent; + } + + private void assertUiTetherProvisioningIntent(int type, final TetheringConfiguration config, + final ResultReceiver receiver, final Intent intent) { + assertEquals(Settings.ACTION_TETHER_PROVISIONING_UI, intent.getAction()); + assertEquals(type, intent.getIntExtra(EXTRA_ADD_TETHER_TYPE, TETHERING_INVALID)); + final String[] appName = intent.getStringArrayExtra( + EXTRA_TETHER_UI_PROVISIONING_APP_NAME); + assertEquals(PROVISIONING_APP_NAME.length, appName.length); + for (int i = 0; i < PROVISIONING_APP_NAME.length; i++) { + assertEquals(PROVISIONING_APP_NAME[i], appName[i]); + } + assertEquals(receiver, intent.getParcelableExtra(EXTRA_PROVISION_CALLBACK)); + assertEquals(config.activeDataSubId, + intent.getIntExtra(EXTRA_TETHER_SUBID, INVALID_SUBSCRIPTION_ID)); } @Override - protected void runSilentTetherProvisioning(int type, int subId) { + protected Intent runSilentTetherProvisioning(int type, + final TetheringConfiguration config) { + Intent intent = super.runSilentTetherProvisioning(type, config); + assertSilentTetherProvisioning(type, config, intent); silentProvisionCount++; addDownstreamMapping(type, fakeEntitlementResult); + return intent; + } + + private void assertSilentTetherProvisioning(int type, final TetheringConfiguration config, + final Intent intent) { + assertEquals(type, intent.getIntExtra(EXTRA_ADD_TETHER_TYPE, TETHERING_INVALID)); + assertEquals(true, intent.getBooleanExtra(EXTRA_RUN_PROVISION, false)); + assertEquals(PROVISIONING_NO_UI_APP_NAME, + intent.getStringExtra(EXTRA_TETHER_SILENT_PROVISIONING_ACTION)); + assertEquals(PROVISIONING_APP_RESPONSE, + intent.getStringExtra(EXTRA_TETHER_PROVISIONING_RESPONSE)); + assertTrue(intent.hasExtra(EXTRA_PROVISION_CALLBACK)); + assertEquals(config.activeDataSubId, + intent.getIntExtra(EXTRA_TETHER_SUBID, INVALID_SUBSCRIPTION_ID)); } } @@ -187,6 +234,8 @@ public final class EntitlementManagerTest { .thenReturn(PROVISIONING_APP_NAME); when(mResources.getString(R.string.config_mobile_hotspot_provision_app_no_ui)) .thenReturn(PROVISIONING_NO_UI_APP_NAME); + when(mResources.getString(R.string.config_mobile_hotspot_provision_response)).thenReturn( + PROVISIONING_APP_RESPONSE); // Act like the CarrierConfigManager is present and ready unless told otherwise. when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE)) .thenReturn(mCarrierConfigManager); diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java index 1999ad786e..312186391d 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java @@ -61,6 +61,8 @@ public class TetheringConfigurationTest { private final SharedLog mLog = new SharedLog("TetheringConfigurationTest"); private static final String[] PROVISIONING_APP_NAME = {"some", "app"}; + private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app"; + private static final String PROVISIONING_APP_RESPONSE = "app_response"; @Mock private Context mContext; @Mock private TelephonyManager mTelephonyManager; @Mock private Resources mResources; @@ -388,6 +390,8 @@ public class TetheringConfigurationTest { new MockTetheringConfiguration(mMockContext, mLog, anyValidSubId); assertEquals(mockCfg.provisioningApp[0], PROVISIONING_APP_NAME[0]); assertEquals(mockCfg.provisioningApp[1], PROVISIONING_APP_NAME[1]); + assertEquals(mockCfg.provisioningAppNoUi, PROVISIONING_NO_UI_APP_NAME); + assertEquals(mockCfg.provisioningResponse, PROVISIONING_APP_RESPONSE); } private void setUpResourceForSubId() { @@ -403,6 +407,10 @@ public class TetheringConfigurationTest { new int[0]); when(mResourcesForSubId.getStringArray( R.array.config_mobile_hotspot_provision_app)).thenReturn(PROVISIONING_APP_NAME); + when(mResourcesForSubId.getString(R.string.config_mobile_hotspot_provision_app_no_ui)) + .thenReturn(PROVISIONING_NO_UI_APP_NAME); + when(mResourcesForSubId.getString( + R.string.config_mobile_hotspot_provision_response)).thenReturn( + PROVISIONING_APP_RESPONSE); } - } From 017bfba0d87dcb37b3cf8c63b6aa77bfbff63447 Mon Sep 17 00:00:00 2001 From: markchien Date: Wed, 3 Jun 2020 12:27:37 +0800 Subject: [PATCH 169/188] Protect invalid entitlement app configuration There is a protection in Settings that Settings would gray out if tethering requires entitlement check but the entitlement app is invalid. Tethering resource is moved from framework to tethering module, so Settings can not fetch entitlement app name anymore. In this change, tethering module would check whether entitltement app package name is exsited if entitlement check is needed. Tethering would be not supported (Settings tethering option would be hidded) if entitlement app is not installed. After moving the protection into tethering module, TetherUtil#isProvisioningNeeded is no longer needed. Because The only use case is Settings wants to gray out tethering setting when entitltement check is needed but entitlement app is invalid. Bug: 146918263 Test: atest TetheringCoverageTests Change-Id: I9a5ff5dbc1db3f3be7fcd7146862a16b373507e6 --- .../networkstack/tethering/Tethering.java | 28 ++++++++++++++--- .../tethering/TetheringDependencies.java | 9 ++++++ .../networkstack/tethering/TetheringTest.java | 30 +++++++++++++++++++ 3 files changed, 63 insertions(+), 4 deletions(-) diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java index df67458550..2f01186b11 100644 --- a/Tethering/src/com/android/networkstack/tethering/Tethering.java +++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java @@ -18,6 +18,7 @@ package com.android.networkstack.tethering; import static android.Manifest.permission.NETWORK_SETTINGS; import static android.Manifest.permission.NETWORK_STACK; +import static android.content.pm.PackageManager.GET_ACTIVITIES; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.hardware.usb.UsbManager.USB_CONFIGURED; import static android.hardware.usb.UsbManager.USB_CONNECTED; @@ -70,6 +71,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.PackageManager; import android.hardware.usb.UsbManager; import android.net.ConnectivityManager; import android.net.EthernetManager; @@ -109,7 +111,6 @@ import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceSpecificException; -import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; @@ -782,11 +783,30 @@ public class Tethering { } } + private boolean isProvisioningNeededButUnavailable() { + return isTetherProvisioningRequired() && !doesEntitlementPackageExist(); + } + boolean isTetherProvisioningRequired() { final TetheringConfiguration cfg = mConfig; return mEntitlementMgr.isTetherProvisioningRequired(cfg); } + private boolean doesEntitlementPackageExist() { + // provisioningApp must contain package and class name. + if (mConfig.provisioningApp.length != 2) { + return false; + } + + final PackageManager pm = mContext.getPackageManager(); + try { + pm.getPackageInfo(mConfig.provisioningApp[0], GET_ACTIVITIES); + } catch (PackageManager.NameNotFoundException e) { + return false; + } + return true; + } + // TODO: Figure out how to update for local hotspot mode interfaces. private void sendTetherStateChangedBroadcast() { if (!isTetheringSupported()) return; @@ -2145,14 +2165,14 @@ public class Tethering { // gservices could set the secure setting to 1 though to enable it on a build where it // had previously been turned off. boolean isTetheringSupported() { - final int defaultVal = - SystemProperties.get("ro.tether.denied").equals("true") ? 0 : 1; + final int defaultVal = mDeps.isTetheringDenied() ? 0 : 1; final boolean tetherSupported = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.TETHER_SUPPORTED, defaultVal) != 0; final boolean tetherEnabledInSettings = tetherSupported && !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING); - return tetherEnabledInSettings && hasTetherableConfiguration(); + return tetherEnabledInSettings && hasTetherableConfiguration() + && !isProvisioningNeededButUnavailable(); } void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer, @Nullable String[] args) { diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java b/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java index d637c8646b..31f747d3c4 100644 --- a/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java @@ -26,6 +26,8 @@ import android.net.util.SharedLog; import android.os.Handler; import android.os.IBinder; import android.os.Looper; +import android.os.SystemProperties; +import android.text.TextUtils; import androidx.annotation.NonNull; @@ -150,4 +152,11 @@ public abstract class TetheringDependencies { * Get a reference to BluetoothAdapter to be used by tethering. */ public abstract BluetoothAdapter getBluetoothAdapter(); + + /** + * Get SystemProperties which indicate whether tethering is denied. + */ + public boolean isTetheringDenied() { + return TextUtils.equals(SystemProperties.get("ro.tether.denied"), "true"); + } } 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 8146a58ddd..f53c42b7e7 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java @@ -16,6 +16,7 @@ package com.android.networkstack.tethering; +import static android.content.pm.PackageManager.GET_ACTIVITIES; import static android.hardware.usb.UsbManager.USB_CONFIGURED; import static android.hardware.usb.UsbManager.USB_CONNECTED; import static android.hardware.usb.UsbManager.USB_FUNCTION_NCM; @@ -81,6 +82,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.content.res.Resources; import android.hardware.usb.UsbManager; import android.net.ConnectivityManager; @@ -204,6 +206,7 @@ public class TetheringTest { @Mock private EthernetManager mEm; @Mock private TetheringNotificationUpdater mNotificationUpdater; @Mock private BpfCoordinator mBpfCoordinator; + @Mock private PackageManager mPackageManager; private final MockIpServerDependencies mIpServerDependencies = spy(new MockIpServerDependencies()); @@ -263,6 +266,11 @@ public class TetheringTest { return super.getSystemService(name); } + @Override + public PackageManager getPackageManager() { + return mPackageManager; + } + @Override public String getSystemServiceName(Class serviceClass) { if (TelephonyManager.class.equals(serviceClass)) return Context.TELEPHONY_SERVICE; @@ -425,6 +433,11 @@ public class TetheringTest { public TetheringNotificationUpdater getNotificationUpdater(Context ctx, Looper looper) { return mNotificationUpdater; } + + @Override + public boolean isTetheringDenied() { + return false; + } } private static UpstreamNetworkState buildMobileUpstreamState(boolean withIPv4, @@ -1951,6 +1964,23 @@ public class TetheringTest { assertEquals(TETHER_ERROR_IFACE_CFG_ERROR, mTethering.getLastTetherError(TEST_ETH_IFNAME)); } + @Test + public void testProvisioningNeededButUnavailable() throws Exception { + assertTrue(mTethering.isTetheringSupported()); + verify(mPackageManager, never()).getPackageInfo(PROVISIONING_APP_NAME[0], GET_ACTIVITIES); + + setupForRequiredProvisioning(); + assertTrue(mTethering.isTetheringSupported()); + verify(mPackageManager).getPackageInfo(PROVISIONING_APP_NAME[0], GET_ACTIVITIES); + reset(mPackageManager); + + doThrow(PackageManager.NameNotFoundException.class).when(mPackageManager).getPackageInfo( + PROVISIONING_APP_NAME[0], GET_ACTIVITIES); + setupForRequiredProvisioning(); + assertFalse(mTethering.isTetheringSupported()); + verify(mPackageManager).getPackageInfo(PROVISIONING_APP_NAME[0], GET_ACTIVITIES); + } + // TODO: Test that a request for hotspot mode doesn't interfere with an // already operating tethering mode interface. } From 2fbc671d4a9ae759cb6495f4f01c50e994efc89e Mon Sep 17 00:00:00 2001 From: Nucca Chen Date: Wed, 17 Jun 2020 06:31:45 +0000 Subject: [PATCH 170/188] [BOT.10] Add unit test for data limit and rule change in BpfCoordinator The applying data limit is based on the forwarding rule changes. Add the tests for verifying their interactions with netd. Bug: 150736748 Test: BpfCoordinatorTest Original-Change: https://android-review.googlesource.com/1311659 Merged-In: I5a98c4cd74e2de6005ee05defa761f6af3fd4e75 Change-Id: I5a98c4cd74e2de6005ee05defa761f6af3fd4e75 --- .../tethering/BpfCoordinatorTest.java | 231 ++++++++++++++++++ 1 file changed, 231 insertions(+) diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java index e2d7aab4e3..ba0f41c4fa 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java @@ -31,10 +31,16 @@ import static com.android.networkstack.tethering.BpfCoordinator.StatsType; import static com.android.networkstack.tethering.BpfCoordinator.StatsType.STATS_PER_IFACE; import static com.android.networkstack.tethering.BpfCoordinator.StatsType.STATS_PER_UID; +import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.fail; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.argThat; import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -42,8 +48,12 @@ import static org.mockito.Mockito.when; import android.annotation.NonNull; import android.app.usage.NetworkStatsManager; import android.net.INetd; +import android.net.InetAddresses; +import android.net.MacAddress; import android.net.NetworkStats; +import android.net.TetherOffloadRuleParcel; import android.net.TetherStatsParcel; +import android.net.ip.IpServer; import android.net.util.SharedLog; import android.os.Handler; import android.os.test.TestLooper; @@ -51,22 +61,37 @@ import android.os.test.TestLooper; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule; import com.android.testutils.TestableNetworkStatsProviderCbBinder; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatcher; +import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.net.Inet6Address; +import java.net.InetAddress; import java.util.ArrayList; +import java.util.Arrays; @RunWith(AndroidJUnit4.class) @SmallTest public class BpfCoordinatorTest { + private static final int DOWNSTREAM_IFINDEX = 10; + private static final MacAddress DOWNSTREAM_MAC = MacAddress.ALL_ZEROS_ADDRESS; + private static final InetAddress NEIGH_A = InetAddresses.parseNumericAddress("2001:db8::1"); + private static final InetAddress NEIGH_B = InetAddresses.parseNumericAddress("2001:db8::2"); + private static final MacAddress MAC_A = MacAddress.fromString("00:00:00:00:00:0a"); + private static final MacAddress MAC_B = MacAddress.fromString("11:22:33:00:00:0b"); + @Mock private NetworkStatsManager mStatsManager; @Mock private INetd mNetd; + @Mock private IpServer mIpServer; + // Late init since methods must be called by the thread that created this object. private TestableNetworkStatsProviderCbBinder mTetherStatsProviderCb; private BpfCoordinator.BpfTetherStatsProvider mTetherStatsProvider; @@ -243,4 +268,210 @@ public class BpfCoordinatorTest { waitForIdle(); mTetherStatsProviderCb.assertNoCallback(); } + + // The custom ArgumentMatcher simply comes from IpServerTest. + // TODO: move both of them into a common utility class for reusing the code. + private static class TetherOffloadRuleParcelMatcher implements + ArgumentMatcher { + public final int upstreamIfindex; + public final int downstreamIfindex; + public final Inet6Address address; + public final MacAddress srcMac; + public final MacAddress dstMac; + + TetherOffloadRuleParcelMatcher(@NonNull Ipv6ForwardingRule rule) { + upstreamIfindex = rule.upstreamIfindex; + downstreamIfindex = rule.downstreamIfindex; + address = rule.address; + srcMac = rule.srcMac; + dstMac = rule.dstMac; + } + + public boolean matches(@NonNull TetherOffloadRuleParcel parcel) { + return upstreamIfindex == parcel.inputInterfaceIndex + && (downstreamIfindex == parcel.outputInterfaceIndex) + && Arrays.equals(address.getAddress(), parcel.destination) + && (128 == parcel.prefixLength) + && Arrays.equals(srcMac.toByteArray(), parcel.srcL2Address) + && Arrays.equals(dstMac.toByteArray(), parcel.dstL2Address); + } + + public String toString() { + return String.format("TetherOffloadRuleParcelMatcher(%d, %d, %s, %s, %s", + upstreamIfindex, downstreamIfindex, address.getHostAddress(), srcMac, dstMac); + } + } + + @NonNull + private TetherOffloadRuleParcel matches(@NonNull Ipv6ForwardingRule rule) { + return argThat(new TetherOffloadRuleParcelMatcher(rule)); + } + + @NonNull + private static Ipv6ForwardingRule buildTestForwardingRule( + int upstreamIfindex, @NonNull InetAddress address, @NonNull MacAddress dstMac) { + return new Ipv6ForwardingRule(upstreamIfindex, DOWNSTREAM_IFINDEX, (Inet6Address) address, + DOWNSTREAM_MAC, dstMac); + } + + @Test + public void testSetDataLimit() throws Exception { + setupFunctioningNetdInterface(); + + final BpfCoordinator coordinator = makeBpfCoordinator(); + + final String mobileIface = "rmnet_data0"; + final Integer mobileIfIndex = 100; + coordinator.addUpstreamNameToLookupTable(mobileIfIndex, mobileIface); + + // [1] Default limit. + // Set the unlimited quota as default if the service has never applied a data limit for a + // given upstream. Note that the data limit only be applied on an upstream which has rules. + final Ipv6ForwardingRule rule = buildTestForwardingRule(mobileIfIndex, NEIGH_A, MAC_A); + final InOrder inOrder = inOrder(mNetd); + coordinator.tetherOffloadRuleAdd(mIpServer, rule); + inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(rule)); + inOrder.verify(mNetd).tetherOffloadSetInterfaceQuota(mobileIfIndex, QUOTA_UNLIMITED); + inOrder.verifyNoMoreInteractions(); + + // [2] Specific limit. + // Applying the data limit boundary {min, max, infinity} on current upstream. + for (final long quota : new long[] {0, Long.MAX_VALUE, QUOTA_UNLIMITED}) { + mTetherStatsProvider.onSetLimit(mobileIface, quota); + waitForIdle(); + inOrder.verify(mNetd).tetherOffloadSetInterfaceQuota(mobileIfIndex, quota); + inOrder.verifyNoMoreInteractions(); + } + + // [3] Invalid limit. + // The valid range of quota is 0..max_int64 or -1 (unlimited). + final long invalidLimit = Long.MIN_VALUE; + try { + mTetherStatsProvider.onSetLimit(mobileIface, invalidLimit); + waitForIdle(); + fail("No exception thrown for invalid limit " + invalidLimit + "."); + } catch (IllegalArgumentException expected) { + assertEquals(expected.getMessage(), "invalid quota value " + invalidLimit); + } + } + + // TODO: Test the case in which the rules are changed from different IpServer objects. + @Test + public void testSetDataLimitOnRuleChange() throws Exception { + setupFunctioningNetdInterface(); + + final BpfCoordinator coordinator = makeBpfCoordinator(); + + final String mobileIface = "rmnet_data0"; + final Integer mobileIfIndex = 100; + coordinator.addUpstreamNameToLookupTable(mobileIfIndex, mobileIface); + + // Applying a data limit to the current upstream does not take any immediate action. + // The data limit could be only set on an upstream which has rules. + final long limit = 12345; + final InOrder inOrder = inOrder(mNetd); + mTetherStatsProvider.onSetLimit(mobileIface, limit); + waitForIdle(); + inOrder.verify(mNetd, never()).tetherOffloadSetInterfaceQuota(anyInt(), anyLong()); + + // Adding the first rule on current upstream immediately sends the quota to netd. + final Ipv6ForwardingRule ruleA = buildTestForwardingRule(mobileIfIndex, NEIGH_A, MAC_A); + coordinator.tetherOffloadRuleAdd(mIpServer, ruleA); + inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(ruleA)); + inOrder.verify(mNetd).tetherOffloadSetInterfaceQuota(mobileIfIndex, limit); + inOrder.verifyNoMoreInteractions(); + + // Adding the second rule on current upstream does not send the quota to netd. + final Ipv6ForwardingRule ruleB = buildTestForwardingRule(mobileIfIndex, NEIGH_B, MAC_B); + coordinator.tetherOffloadRuleAdd(mIpServer, ruleB); + inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(ruleB)); + inOrder.verify(mNetd, never()).tetherOffloadSetInterfaceQuota(anyInt(), anyLong()); + + // Removing the second rule on current upstream does not send the quota to netd. + coordinator.tetherOffloadRuleRemove(mIpServer, ruleB); + inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(ruleB)); + inOrder.verify(mNetd, never()).tetherOffloadSetInterfaceQuota(anyInt(), anyLong()); + + // Removing the last rule on current upstream immediately sends the cleanup stuff to netd. + when(mNetd.tetherOffloadGetAndClearStats(mobileIfIndex)) + .thenReturn(buildTestTetherStatsParcel(mobileIfIndex, 0, 0, 0, 0)); + coordinator.tetherOffloadRuleRemove(mIpServer, ruleA); + inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(ruleA)); + inOrder.verify(mNetd).tetherOffloadGetAndClearStats(mobileIfIndex); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testTetherOffloadRuleUpdateAndClear() throws Exception { + setupFunctioningNetdInterface(); + + final BpfCoordinator coordinator = makeBpfCoordinator(); + + final String ethIface = "eth1"; + final String mobileIface = "rmnet_data0"; + final Integer ethIfIndex = 100; + final Integer mobileIfIndex = 101; + coordinator.addUpstreamNameToLookupTable(ethIfIndex, ethIface); + coordinator.addUpstreamNameToLookupTable(mobileIfIndex, mobileIface); + + final InOrder inOrder = inOrder(mNetd); + + // Before the rule test, here are the additional actions while the rules are changed. + // - After adding the first rule on a given upstream, the coordinator adds a data limit. + // If the service has never applied the data limit, set an unlimited quota as default. + // - After removing the last rule on a given upstream, the coordinator gets the last stats. + // Then, it clears the stats and the limit entry from BPF maps. + // See tetherOffloadRule{Add, Remove, Clear, Clean}. + + // [1] Adding rules on the upstream Ethernet. + // Note that the default data limit is applied after the first rule is added. + final Ipv6ForwardingRule ethernetRuleA = buildTestForwardingRule( + ethIfIndex, NEIGH_A, MAC_A); + final Ipv6ForwardingRule ethernetRuleB = buildTestForwardingRule( + ethIfIndex, NEIGH_B, MAC_B); + + coordinator.tetherOffloadRuleAdd(mIpServer, ethernetRuleA); + inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(ethernetRuleA)); + inOrder.verify(mNetd).tetherOffloadSetInterfaceQuota(ethIfIndex, QUOTA_UNLIMITED); + + coordinator.tetherOffloadRuleAdd(mIpServer, ethernetRuleB); + inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(ethernetRuleB)); + + // [2] Update the existing rules from Ethernet to cellular. + final Ipv6ForwardingRule mobileRuleA = buildTestForwardingRule( + mobileIfIndex, NEIGH_A, MAC_A); + final Ipv6ForwardingRule mobileRuleB = buildTestForwardingRule( + mobileIfIndex, NEIGH_B, MAC_B); + when(mNetd.tetherOffloadGetAndClearStats(ethIfIndex)) + .thenReturn(buildTestTetherStatsParcel(ethIfIndex, 10, 20, 30, 40)); + + // Update the existing rules for upstream changes. The rules are removed and re-added one + // by one for updating upstream interface index by #tetherOffloadRuleUpdate. + coordinator.tetherOffloadRuleUpdate(mIpServer, mobileIfIndex); + inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(ethernetRuleA)); + inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(mobileRuleA)); + inOrder.verify(mNetd).tetherOffloadSetInterfaceQuota(mobileIfIndex, QUOTA_UNLIMITED); + inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(ethernetRuleB)); + inOrder.verify(mNetd).tetherOffloadGetAndClearStats(ethIfIndex); + inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(mobileRuleB)); + + // [3] Clear all rules for a given IpServer. + when(mNetd.tetherOffloadGetAndClearStats(mobileIfIndex)) + .thenReturn(buildTestTetherStatsParcel(mobileIfIndex, 50, 60, 70, 80)); + coordinator.tetherOffloadRuleClear(mIpServer); + inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(mobileRuleA)); + inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(mobileRuleB)); + inOrder.verify(mNetd).tetherOffloadGetAndClearStats(mobileIfIndex); + + // [4] Force pushing stats update to verify that the last diff of stats is reported on all + // upstreams. + mTetherStatsProvider.pushTetherStats(); + mTetherStatsProviderCb.expectNotifyStatsUpdated( + new NetworkStats(0L, 2) + .addEntry(buildTestEntry(STATS_PER_IFACE, ethIface, 10, 20, 30, 40)) + .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 50, 60, 70, 80)), + new NetworkStats(0L, 2) + .addEntry(buildTestEntry(STATS_PER_UID, ethIface, 10, 20, 30, 40)) + .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 50, 60, 70, 80))); + } } From a340f25d3ee2dcdac66ee9b3ffd39ee5c8a9a84d Mon Sep 17 00:00:00 2001 From: Nucca Chen Date: Wed, 17 Jun 2020 06:32:35 +0000 Subject: [PATCH 171/188] [BOT.8] Dump BPF offload information in dumpsys $ adb shell dumpsys tethering BPF offload: Polling started Stats provider registered Upstream quota: {rmnet_data2=9223372036854775807} Forwarding stats: 12(rmnet_data2) - ForwardedStats(rxb: 1065, rxp: 5, txb: 0, txp: 0) Forwarding rules: [wlan1]: iif(iface) oif(iface) v6addr srcmac dstmac 12(rmnet_data2) 31(wlan1) /2401:e180:8831:77ae:a900:a03b:41fb.. Bug: 150736748 Test: Enable tethering on mobile data and check dumpsys tethering Original-Change: https://android-review.googlesource.com/1302438 Merged-In: I95ea3050d92f3ba8136a63cd399d3450d183c8dc Change-Id: I95ea3050d92f3ba8136a63cd399d3450d183c8dc --- .../tethering/BpfCoordinator.java | 73 +++++++++++++++++++ .../networkstack/tethering/Tethering.java | 5 ++ 2 files changed, 78 insertions(+) diff --git a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java index fc27b6add0..4315485f06 100644 --- a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java +++ b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java @@ -36,6 +36,7 @@ import android.net.ip.IpServer; import android.net.netstats.provider.NetworkStatsProvider; import android.net.util.SharedLog; import android.net.util.TetheringUtils.ForwardedStats; +import android.os.ConditionVariable; import android.os.Handler; import android.os.RemoteException; import android.os.ServiceSpecificException; @@ -47,11 +48,13 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.IndentingPrintWriter; import java.net.Inet6Address; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; +import java.util.Map; import java.util.Objects; /** @@ -65,6 +68,7 @@ import java.util.Objects; */ public class BpfCoordinator { private static final String TAG = BpfCoordinator.class.getSimpleName(); + private static final int DUMP_TIMEOUT_MS = 10_000; @VisibleForTesting static final int DEFAULT_PERFORM_POLL_INTERVAL_MS = 5000; // TODO: Make it customizable. @@ -344,6 +348,75 @@ public class BpfCoordinator { } } + /** + * Dump information. + * Block the function until all the data are dumped on the handler thread or timed-out. The + * reason is that dumpsys invokes this function on the thread of caller and the data may only + * be allowed to be accessed on the handler thread. + */ + public void dump(@NonNull IndentingPrintWriter pw) { + final ConditionVariable dumpDone = new ConditionVariable(); + mHandler.post(() -> { + pw.println("Polling " + (mPollingStarted ? "started" : "not started")); + pw.println("Stats provider " + (mStatsProvider != null + ? "registered" : "not registered")); + pw.println("Upstream quota: " + mInterfaceQuotas.toString()); + + pw.println("Forwarding stats:"); + pw.increaseIndent(); + if (mStats.size() == 0) { + pw.println(""); + } else { + dumpStats(pw); + } + pw.decreaseIndent(); + + pw.println("Forwarding rules:"); + pw.increaseIndent(); + if (mIpv6ForwardingRules.size() == 0) { + pw.println(""); + } else { + dumpIpv6ForwardingRules(pw); + } + pw.decreaseIndent(); + + dumpDone.open(); + }); + if (!dumpDone.block(DUMP_TIMEOUT_MS)) { + pw.println("... dump timed-out after " + DUMP_TIMEOUT_MS + "ms"); + } + } + + private void dumpStats(@NonNull IndentingPrintWriter pw) { + for (int i = 0; i < mStats.size(); i++) { + final int upstreamIfindex = mStats.keyAt(i); + final ForwardedStats stats = mStats.get(upstreamIfindex); + pw.println(String.format("%d(%s) - %s", upstreamIfindex, mInterfaceNames.get( + upstreamIfindex), stats.toString())); + } + } + + private void dumpIpv6ForwardingRules(@NonNull IndentingPrintWriter pw) { + for (Map.Entry> entry : + mIpv6ForwardingRules.entrySet()) { + IpServer ipServer = entry.getKey(); + // The rule downstream interface index is paired with the interface name from + // IpServer#interfaceName. See #startIPv6, #updateIpv6ForwardingRules in IpServer. + final String downstreamIface = ipServer.interfaceName(); + pw.println("[" + downstreamIface + "]: iif(iface) oif(iface) v6addr srcmac dstmac"); + + pw.increaseIndent(); + LinkedHashMap rules = entry.getValue(); + for (Ipv6ForwardingRule rule : rules.values()) { + final int upstreamIfindex = rule.upstreamIfindex; + pw.println(String.format("%d(%s) %d(%s) %s %s %s", upstreamIfindex, + mInterfaceNames.get(upstreamIfindex), rule.downstreamIfindex, + downstreamIface, rule.address, rule.srcMac, rule.dstMac)); + } + pw.decreaseIndent(); + } + } + /** IPv6 forwarding rule class. */ public static class Ipv6ForwardingRule { public final int upstreamIfindex; diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java index 2f01186b11..3184b1ffed 100644 --- a/Tethering/src/com/android/networkstack/tethering/Tethering.java +++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java @@ -2236,6 +2236,11 @@ public class Tethering { mOffloadController.dump(pw); pw.decreaseIndent(); + pw.println("BPF offload:"); + pw.increaseIndent(); + mBpfCoordinator.dump(pw); + pw.decreaseIndent(); + pw.println("Private address coordinator:"); pw.increaseIndent(); mPrivateAddressCoordinator.dump(pw); From d105961e583aca5ff84a84c4451619297591e7fc Mon Sep 17 00:00:00 2001 From: Nucca Chen Date: Wed, 17 Jun 2020 07:03:39 +0000 Subject: [PATCH 172/188] [BOT.11] BpfCoordinator could be disabled by device config Bug: 150736748 Test: BpfCoordinatorTest Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1313955 Merged-In: Id413b7f2f7edb2e5c3e02d5677fe536ed52fbbcb Change-Id: I48a8de478c9200b8c9f88e785340fc7973e4a13f (cherry picked from commit fe737bc2b26b91558bb1aa09f42f35c5af07ee66) --- .../tethering/BpfCoordinator.java | 65 ++++++++++++++++--- .../networkstack/tethering/Tethering.java | 34 +++++++++- .../tethering/TetheringDependencies.java | 5 +- .../unit/src/android/net/ip/IpServerTest.java | 42 ++++++++++-- .../tethering/BpfCoordinatorTest.java | 41 ++++++++++-- .../networkstack/tethering/TetheringTest.java | 4 +- 6 files changed, 162 insertions(+), 29 deletions(-) diff --git a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java index 4315485f06..aa1d59de89 100644 --- a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java +++ b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java @@ -89,6 +89,13 @@ public class BpfCoordinator { @Nullable private final BpfTetherStatsProvider mStatsProvider; + // True if BPF offload is supported, false otherwise. The BPF offload could be disabled by + // a runtime resource overlay package or device configuration. This flag is only initialized + // in the constructor because it is hard to unwind all existing change once device + // configuration is changed. Especially the forwarding rules. Keep the same setting + // to make it simpler. See also TetheringConfiguration. + private final boolean mUsingBpf; + // Tracks whether BPF tethering is started or not. This is set by tethering before it // starts the first IpServer and is cleared by tethering shortly before the last IpServer // is stopped. Note that rule updates (especially deletions, but sometimes additions as @@ -146,22 +153,42 @@ public class BpfCoordinator { }; @VisibleForTesting - public static class Dependencies { - int getPerformPollInterval() { + public abstract static class Dependencies { + /** + * Get polling Interval in milliseconds. + */ + public int getPerformPollInterval() { // TODO: Consider make this configurable. return DEFAULT_PERFORM_POLL_INTERVAL_MS; } + + /** Get handler. */ + @NonNull public abstract Handler getHandler(); + + /** Get netd. */ + @NonNull public abstract INetd getNetd(); + + /** Get network stats manager. */ + @NonNull public abstract NetworkStatsManager getNetworkStatsManager(); + + /** Get shared log. */ + @NonNull public abstract SharedLog getSharedLog(); + + /** Get tethering configuration. */ + @Nullable public abstract TetheringConfiguration getTetherConfig(); } @VisibleForTesting - public BpfCoordinator(@NonNull Handler handler, @NonNull INetd netd, - @NonNull NetworkStatsManager nsm, @NonNull SharedLog log, @NonNull Dependencies deps) { - mHandler = handler; - mNetd = netd; - mLog = log.forSubComponent(TAG); + public BpfCoordinator(@NonNull Dependencies deps) { + mDeps = deps; + mHandler = mDeps.getHandler(); + mNetd = mDeps.getNetd(); + mLog = mDeps.getSharedLog().forSubComponent(TAG); + mUsingBpf = isOffloadEnabled(); BpfTetherStatsProvider provider = new BpfTetherStatsProvider(); try { - nsm.registerNetworkStatsProvider(getClass().getSimpleName(), provider); + mDeps.getNetworkStatsManager().registerNetworkStatsProvider( + getClass().getSimpleName(), provider); } catch (RuntimeException e) { // TODO: Perhaps not allow to use BPF offload because the reregistration failure // implied that no data limit could be applies on a metered upstream if any. @@ -169,7 +196,6 @@ public class BpfCoordinator { provider = null; } mStatsProvider = provider; - mDeps = deps; } /** @@ -181,6 +207,11 @@ public class BpfCoordinator { public void startPolling() { if (mPollingStarted) return; + if (!mUsingBpf) { + mLog.i("Offload disabled"); + return; + } + mPollingStarted = true; maybeSchedulePollingStats(); @@ -215,6 +246,8 @@ public class BpfCoordinator { */ public void tetherOffloadRuleAdd( @NonNull final IpServer ipServer, @NonNull final Ipv6ForwardingRule rule) { + if (!mUsingBpf) return; + try { // TODO: Perhaps avoid to add a duplicate rule. mNetd.tetherOffloadRuleAdd(rule.toTetherOffloadRuleParcel()); @@ -254,6 +287,8 @@ public class BpfCoordinator { */ public void tetherOffloadRuleRemove( @NonNull final IpServer ipServer, @NonNull final Ipv6ForwardingRule rule) { + if (!mUsingBpf) return; + try { // TODO: Perhaps avoid to remove a non-existent rule. mNetd.tetherOffloadRuleRemove(rule.toTetherOffloadRuleParcel()); @@ -297,6 +332,8 @@ public class BpfCoordinator { * Note that this can be only called on handler thread. */ public void tetherOffloadRuleClear(@NonNull final IpServer ipServer) { + if (!mUsingBpf) return; + final LinkedHashMap rules = mIpv6ForwardingRules.get( ipServer); if (rules == null) return; @@ -312,6 +349,8 @@ public class BpfCoordinator { * Note that this can be only called on handler thread. */ public void tetherOffloadRuleUpdate(@NonNull final IpServer ipServer, int newUpstreamIfindex) { + if (!mUsingBpf) return; + final LinkedHashMap rules = mIpv6ForwardingRules.get( ipServer); if (rules == null) return; @@ -334,6 +373,8 @@ public class BpfCoordinator { * Note that this can be only called on handler thread. */ public void addUpstreamNameToLookupTable(int upstreamIfindex, @NonNull String upstreamIface) { + if (!mUsingBpf) return; + if (upstreamIfindex == 0 || TextUtils.isEmpty(upstreamIface)) return; // The same interface index to name mapping may be added by different IpServer objects or @@ -357,6 +398,7 @@ public class BpfCoordinator { public void dump(@NonNull IndentingPrintWriter pw) { final ConditionVariable dumpDone = new ConditionVariable(); mHandler.post(() -> { + pw.println("mUsingBpf: " + mUsingBpf); pw.println("Polling " + (mPollingStarted ? "started" : "not started")); pw.println("Stats provider " + (mStatsProvider != null ? "registered" : "not registered")); @@ -547,6 +589,11 @@ public class BpfCoordinator { } } + private boolean isOffloadEnabled() { + final TetheringConfiguration config = mDeps.getTetherConfig(); + return (config != null) ? config.enableBpfOffload : true /* default value */; + } + private int getInterfaceIndexFromRules(@NonNull String ifName) { for (LinkedHashMap rules : mIpv6ForwardingRules .values()) { diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java index 3184b1ffed..99ffa774b5 100644 --- a/Tethering/src/com/android/networkstack/tethering/Tethering.java +++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java @@ -63,6 +63,7 @@ import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import static com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE; +import android.app.usage.NetworkStatsManager; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothPan; import android.bluetooth.BluetoothProfile; @@ -286,8 +287,6 @@ public class Tethering { mUpstreamNetworkMonitor = mDeps.getUpstreamNetworkMonitor(mContext, mTetherMasterSM, mLog, TetherMasterSM.EVENT_UPSTREAM_CALLBACK); mForwardedDownstreams = new LinkedHashSet<>(); - mBpfCoordinator = mDeps.getBpfCoordinator( - mHandler, mNetd, mLog, new BpfCoordinator.Dependencies()); IntentFilter filter = new IntentFilter(); filter.addAction(ACTION_CARRIER_CONFIG_CHANGED); @@ -325,6 +324,37 @@ public class Tethering { // Load tethering configuration. updateConfiguration(); + // Must be initialized after tethering configuration is loaded because BpfCoordinator + // constructor needs to use the configuration. + mBpfCoordinator = mDeps.getBpfCoordinator( + new BpfCoordinator.Dependencies() { + @NonNull + public Handler getHandler() { + return mHandler; + } + + @NonNull + public INetd getNetd() { + return mNetd; + } + + @NonNull + public NetworkStatsManager getNetworkStatsManager() { + return (NetworkStatsManager) mContext.getSystemService( + Context.NETWORK_STATS_SERVICE); + } + + @NonNull + public SharedLog getSharedLog() { + return mLog; + } + + @Nullable + public TetheringConfiguration getTetherConfig() { + return mConfig; + } + }); + startStateMachineUpdaters(); } diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java b/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java index 31f747d3c4..131a5fbf2a 100644 --- a/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java @@ -46,11 +46,8 @@ public abstract class TetheringDependencies { * Get a reference to the BpfCoordinator to be used by tethering. */ public @NonNull BpfCoordinator getBpfCoordinator( - @NonNull Handler handler, @NonNull INetd netd, @NonNull SharedLog log, @NonNull BpfCoordinator.Dependencies deps) { - final NetworkStatsManager statsManager = - (NetworkStatsManager) getContext().getSystemService(Context.NETWORK_STATS_SERVICE); - return new BpfCoordinator(handler, netd, statsManager, log, deps); + return new BpfCoordinator(deps); } /** diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java index c3bc915a23..fdfd92617b 100644 --- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java +++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java @@ -89,12 +89,14 @@ import android.os.test.TestLooper; import android.text.TextUtils; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.networkstack.tethering.BpfCoordinator; import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule; import com.android.networkstack.tethering.PrivateAddressCoordinator; +import com.android.networkstack.tethering.TetheringConfiguration; import org.junit.Before; import org.junit.Test; @@ -226,9 +228,36 @@ public class IpServerTest { when(mSharedLog.forSubComponent(anyString())).thenReturn(mSharedLog); when(mAddressCoordinator.requestDownstreamAddress(any())).thenReturn(mTestAddress); - BpfCoordinator bc = new BpfCoordinator(new Handler(mLooper.getLooper()), mNetd, - mStatsManager, mSharedLog, new BpfCoordinator.Dependencies()); - mBpfCoordinator = spy(bc); + mBpfCoordinator = spy(new BpfCoordinator( + new BpfCoordinator.Dependencies() { + @NonNull + public Handler getHandler() { + return new Handler(mLooper.getLooper()); + } + + @NonNull + public INetd getNetd() { + return mNetd; + } + + @NonNull + public NetworkStatsManager getNetworkStatsManager() { + return mStatsManager; + } + + @NonNull + public SharedLog getSharedLog() { + return mSharedLog; + } + + @Nullable + public TetheringConfiguration getTetherConfig() { + // Returning null configuration object is a hack to enable BPF offload. + // See BpfCoordinator#isOffloadEnabled. + // TODO: Mock TetheringConfiguration to test. + return null; + } + })); } @Test @@ -671,18 +700,21 @@ public class IpServerTest { } } - private TetherOffloadRuleParcel matches( + @NonNull + private static TetherOffloadRuleParcel matches( int upstreamIfindex, InetAddress dst, MacAddress dstMac) { return argThat(new TetherOffloadRuleParcelMatcher(upstreamIfindex, dst, dstMac)); } + @NonNull private static Ipv6ForwardingRule makeForwardingRule( int upstreamIfindex, @NonNull InetAddress dst, @NonNull MacAddress dstMac) { return new Ipv6ForwardingRule(upstreamIfindex, TEST_IFACE_PARAMS.index, (Inet6Address) dst, TEST_IFACE_PARAMS.macAddr, dstMac); } - private TetherStatsParcel buildEmptyTetherStatsParcel(int ifIndex) { + @NonNull + private static TetherStatsParcel buildEmptyTetherStatsParcel(int ifIndex) { TetherStatsParcel parcel = new TetherStatsParcel(); parcel.ifIndex = ifIndex; return parcel; diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java index ba0f41c4fa..f63a473f1d 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java @@ -45,7 +45,6 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.annotation.NonNull; import android.app.usage.NetworkStatsManager; import android.net.INetd; import android.net.InetAddresses; @@ -58,6 +57,8 @@ import android.net.util.SharedLog; import android.os.Handler; import android.os.test.TestLooper; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -101,9 +102,37 @@ public class BpfCoordinatorTest { private BpfCoordinator.Dependencies mDeps = new BpfCoordinator.Dependencies() { @Override - int getPerformPollInterval() { + public int getPerformPollInterval() { return DEFAULT_PERFORM_POLL_INTERVAL_MS; } + + @NonNull + public Handler getHandler() { + return new Handler(mTestLooper.getLooper()); + } + + @NonNull + public INetd getNetd() { + return mNetd; + } + + @NonNull + public NetworkStatsManager getNetworkStatsManager() { + return mStatsManager; + } + + @NonNull + public SharedLog getSharedLog() { + return new SharedLog("test"); + } + + @Nullable + public TetheringConfiguration getTetherConfig() { + // Returning null configuration object is a hack to enable BPF offload. + // See BpfCoordinator#isOffloadEnabled. + // TODO: Mock TetheringConfiguration to test. + return null; + } }; @Before public void setUp() { @@ -120,9 +149,7 @@ public class BpfCoordinatorTest { @NonNull private BpfCoordinator makeBpfCoordinator() throws Exception { - BpfCoordinator coordinator = new BpfCoordinator( - new Handler(mTestLooper.getLooper()), mNetd, mStatsManager, new SharedLog("test"), - mDeps); + final BpfCoordinator coordinator = new BpfCoordinator(mDeps); final ArgumentCaptor tetherStatsProviderCaptor = ArgumentCaptor.forClass(BpfCoordinator.BpfTetherStatsProvider.class); @@ -335,8 +362,8 @@ public class BpfCoordinatorTest { inOrder.verifyNoMoreInteractions(); // [2] Specific limit. - // Applying the data limit boundary {min, max, infinity} on current upstream. - for (final long quota : new long[] {0, Long.MAX_VALUE, QUOTA_UNLIMITED}) { + // Applying the data limit boundary {min, 1gb, max, infinity} on current upstream. + for (final long quota : new long[] {0, 1048576000, Long.MAX_VALUE, QUOTA_UNLIMITED}) { mTetherStatsProvider.onSetLimit(mobileIface, quota); waitForIdle(); inOrder.verify(mNetd).tetherOffloadSetInterfaceQuota(mobileIfIndex, quota); 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 f53c42b7e7..526199226a 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java @@ -346,8 +346,8 @@ public class TetheringTest { } @Override - public BpfCoordinator getBpfCoordinator(Handler handler, INetd netd, - SharedLog log, BpfCoordinator.Dependencies deps) { + public BpfCoordinator getBpfCoordinator( + BpfCoordinator.Dependencies deps) { return mBpfCoordinator; } From 0a0d4e156d4dbdd11aaedebc904d6d403a433cfb Mon Sep 17 00:00:00 2001 From: Nucca Chen Date: Wed, 17 Jun 2020 07:03:57 +0000 Subject: [PATCH 173/188] [BOT.12] Add unit test for disabling BpfCoordinator by config Bug: 150736748 Test: BpfCoordinatorTest Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1313802 Merged-In: Iedb936b7592b6be773d1b84a2498bfc5a440a198 Change-Id: I298ae39a1fa61b2cf97752aa908aa2d7d0f9783d (cherry picked from commit 3aba923ce2d52803a32143ef25ecaf9d43a6993c) --- .../tethering/BpfCoordinator.java | 39 +++++++--- .../networkstack/tethering/Tethering.java | 5 +- .../tethering/TetheringConfiguration.java | 14 ++-- .../unit/src/android/net/ip/IpServerTest.java | 7 +- .../tethering/BpfCoordinatorTest.java | 74 +++++++++++++++++-- .../tethering/TetheringConfigurationTest.java | 8 +- 6 files changed, 112 insertions(+), 35 deletions(-) diff --git a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java index aa1d59de89..12464e1289 100644 --- a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java +++ b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java @@ -94,7 +94,7 @@ public class BpfCoordinator { // in the constructor because it is hard to unwind all existing change once device // configuration is changed. Especially the forwarding rules. Keep the same setting // to make it simpler. See also TetheringConfiguration. - private final boolean mUsingBpf; + private final boolean mIsBpfEnabled; // Tracks whether BPF tethering is started or not. This is set by tethering before it // starts the first IpServer and is cleared by tethering shortly before the last IpServer @@ -184,7 +184,7 @@ public class BpfCoordinator { mHandler = mDeps.getHandler(); mNetd = mDeps.getNetd(); mLog = mDeps.getSharedLog().forSubComponent(TAG); - mUsingBpf = isOffloadEnabled(); + mIsBpfEnabled = isBpfEnabled(); BpfTetherStatsProvider provider = new BpfTetherStatsProvider(); try { mDeps.getNetworkStatsManager().registerNetworkStatsProvider( @@ -207,7 +207,7 @@ public class BpfCoordinator { public void startPolling() { if (mPollingStarted) return; - if (!mUsingBpf) { + if (!mIsBpfEnabled) { mLog.i("Offload disabled"); return; } @@ -246,7 +246,7 @@ public class BpfCoordinator { */ public void tetherOffloadRuleAdd( @NonNull final IpServer ipServer, @NonNull final Ipv6ForwardingRule rule) { - if (!mUsingBpf) return; + if (!mIsBpfEnabled) return; try { // TODO: Perhaps avoid to add a duplicate rule. @@ -287,7 +287,7 @@ public class BpfCoordinator { */ public void tetherOffloadRuleRemove( @NonNull final IpServer ipServer, @NonNull final Ipv6ForwardingRule rule) { - if (!mUsingBpf) return; + if (!mIsBpfEnabled) return; try { // TODO: Perhaps avoid to remove a non-existent rule. @@ -332,7 +332,7 @@ public class BpfCoordinator { * Note that this can be only called on handler thread. */ public void tetherOffloadRuleClear(@NonNull final IpServer ipServer) { - if (!mUsingBpf) return; + if (!mIsBpfEnabled) return; final LinkedHashMap rules = mIpv6ForwardingRules.get( ipServer); @@ -349,7 +349,7 @@ public class BpfCoordinator { * Note that this can be only called on handler thread. */ public void tetherOffloadRuleUpdate(@NonNull final IpServer ipServer, int newUpstreamIfindex) { - if (!mUsingBpf) return; + if (!mIsBpfEnabled) return; final LinkedHashMap rules = mIpv6ForwardingRules.get( ipServer); @@ -373,7 +373,7 @@ public class BpfCoordinator { * Note that this can be only called on handler thread. */ public void addUpstreamNameToLookupTable(int upstreamIfindex, @NonNull String upstreamIface) { - if (!mUsingBpf) return; + if (!mIsBpfEnabled) return; if (upstreamIfindex == 0 || TextUtils.isEmpty(upstreamIface)) return; @@ -398,7 +398,7 @@ public class BpfCoordinator { public void dump(@NonNull IndentingPrintWriter pw) { final ConditionVariable dumpDone = new ConditionVariable(); mHandler.post(() -> { - pw.println("mUsingBpf: " + mUsingBpf); + pw.println("mIsBpfEnabled: " + mIsBpfEnabled); pw.println("Polling " + (mPollingStarted ? "started" : "not started")); pw.println("Stats provider " + (mStatsProvider != null ? "registered" : "not registered")); @@ -589,9 +589,9 @@ public class BpfCoordinator { } } - private boolean isOffloadEnabled() { + private boolean isBpfEnabled() { final TetheringConfiguration config = mDeps.getTetherConfig(); - return (config != null) ? config.enableBpfOffload : true /* default value */; + return (config != null) ? config.isBpfOffloadEnabled() : true /* default value */; } private int getInterfaceIndexFromRules(@NonNull String ifName) { @@ -754,4 +754,21 @@ public class BpfCoordinator { mHandler.postDelayed(mScheduledPollingTask, mDeps.getPerformPollInterval()); } + + // Return forwarding rule map. This is used for testing only. + // Note that this can be only called on handler thread. + @NonNull + @VisibleForTesting + final HashMap> + getForwardingRulesForTesting() { + return mIpv6ForwardingRules; + } + + // Return upstream interface name map. This is used for testing only. + // Note that this can be only called on handler thread. + @NonNull + @VisibleForTesting + final SparseArray getInterfaceNamesForTesting() { + return mInterfaceNames; + } } diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java index 99ffa774b5..c72ac52740 100644 --- a/Tethering/src/com/android/networkstack/tethering/Tethering.java +++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java @@ -340,8 +340,7 @@ public class Tethering { @NonNull public NetworkStatsManager getNetworkStatsManager() { - return (NetworkStatsManager) mContext.getSystemService( - Context.NETWORK_STATS_SERVICE); + return mContext.getSystemService(NetworkStatsManager.class); } @NonNull @@ -2405,7 +2404,7 @@ public class Tethering { final TetherState tetherState = new TetherState( new IpServer(iface, mLooper, interfaceType, mLog, mNetd, mBpfCoordinator, makeControlCallback(), mConfig.enableLegacyDhcpServer, - mConfig.enableBpfOffload, mPrivateAddressCoordinator, + mConfig.isBpfOffloadEnabled(), mPrivateAddressCoordinator, mDeps.getIpServerDependencies())); mTetherStates.put(iface, tetherState); tetherState.ipServer.start(); diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java index 1d45f129b5..18b2b7804f 100644 --- a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java @@ -101,8 +101,6 @@ public class TetheringConfiguration { public final String[] legacyDhcpRanges; public final String[] defaultIPv4DNS; public final boolean enableLegacyDhcpServer; - // TODO: Add to TetheringConfigurationParcel if required. - public final boolean enableBpfOffload; public final String[] provisioningApp; public final String provisioningAppNoUi; @@ -112,6 +110,8 @@ public class TetheringConfiguration { public final int activeDataSubId; private final int mOffloadPollInterval; + // TODO: Add to TetheringConfigurationParcel if required. + private final boolean mEnableBpfOffload; public TetheringConfiguration(Context ctx, SharedLog log, int id) { final SharedLog configLog = log.forSubComponent("config"); @@ -138,7 +138,7 @@ public class TetheringConfiguration { legacyDhcpRanges = getLegacyDhcpRanges(res); defaultIPv4DNS = copy(DEFAULT_IPV4_DNS); - enableBpfOffload = getEnableBpfOffload(res); + mEnableBpfOffload = getEnableBpfOffload(res); enableLegacyDhcpServer = getEnableLegacyDhcpServer(res); provisioningApp = getResourceStringArray(res, R.array.config_mobile_hotspot_provision_app); @@ -222,7 +222,7 @@ public class TetheringConfiguration { pw.println(provisioningAppNoUi); pw.print("enableBpfOffload: "); - pw.println(enableBpfOffload); + pw.println(mEnableBpfOffload); pw.print("enableLegacyDhcpServer: "); pw.println(enableLegacyDhcpServer); @@ -244,7 +244,7 @@ public class TetheringConfiguration { toIntArray(preferredUpstreamIfaceTypes))); sj.add(String.format("provisioningApp:%s", makeString(provisioningApp))); sj.add(String.format("provisioningAppNoUi:%s", provisioningAppNoUi)); - sj.add(String.format("enableBpfOffload:%s", enableBpfOffload)); + sj.add(String.format("enableBpfOffload:%s", mEnableBpfOffload)); sj.add(String.format("enableLegacyDhcpServer:%s", enableLegacyDhcpServer)); return String.format("TetheringConfiguration{%s}", sj.toString()); } @@ -283,6 +283,10 @@ public class TetheringConfiguration { return mOffloadPollInterval; } + public boolean isBpfOffloadEnabled() { + return mEnableBpfOffload; + } + private static Collection getUpstreamIfaceTypes(Resources res, boolean dunRequired) { final int[] ifaceTypes = res.getIntArray(R.array.config_tether_upstream_types); final ArrayList upstreamIfaceTypes = new ArrayList<>(ifaceTypes.length); diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java index fdfd92617b..4f88605391 100644 --- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java +++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java @@ -144,6 +144,7 @@ public class IpServerTest { @Mock private IpServer.Dependencies mDependencies; @Mock private PrivateAddressCoordinator mAddressCoordinator; @Mock private NetworkStatsManager mStatsManager; + @Mock private TetheringConfiguration mTetherConfig; @Captor private ArgumentCaptor mDhcpParamsCaptor; @@ -227,6 +228,7 @@ public class IpServerTest { MockitoAnnotations.initMocks(this); when(mSharedLog.forSubComponent(anyString())).thenReturn(mSharedLog); when(mAddressCoordinator.requestDownstreamAddress(any())).thenReturn(mTestAddress); + when(mTetherConfig.isBpfOffloadEnabled()).thenReturn(true /* default value */); mBpfCoordinator = spy(new BpfCoordinator( new BpfCoordinator.Dependencies() { @@ -252,10 +254,7 @@ public class IpServerTest { @Nullable public TetheringConfiguration getTetherConfig() { - // Returning null configuration object is a hack to enable BPF offload. - // See BpfCoordinator#isOffloadEnabled. - // TODO: Mock TetheringConfiguration to test. - return null; + return mTetherConfig; } })); } diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java index f63a473f1d..31d98147c7 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java @@ -31,10 +31,11 @@ import static com.android.networkstack.tethering.BpfCoordinator.StatsType; import static com.android.networkstack.tethering.BpfCoordinator.StatsType.STATS_PER_IFACE; import static com.android.networkstack.tethering.BpfCoordinator.StatsType.STATS_PER_UID; -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertNotNull; -import static junit.framework.Assert.fail; - +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.anyString; @@ -78,6 +79,7 @@ import java.net.Inet6Address; import java.net.InetAddress; import java.util.ArrayList; import java.util.Arrays; +import java.util.LinkedHashMap; @RunWith(AndroidJUnit4.class) @SmallTest @@ -92,6 +94,7 @@ public class BpfCoordinatorTest { @Mock private NetworkStatsManager mStatsManager; @Mock private INetd mNetd; @Mock private IpServer mIpServer; + @Mock private TetheringConfiguration mTetherConfig; // Late init since methods must be called by the thread that created this object. private TestableNetworkStatsProviderCbBinder mTetherStatsProviderCb; @@ -128,15 +131,13 @@ public class BpfCoordinatorTest { @Nullable public TetheringConfiguration getTetherConfig() { - // Returning null configuration object is a hack to enable BPF offload. - // See BpfCoordinator#isOffloadEnabled. - // TODO: Mock TetheringConfiguration to test. - return null; + return mTetherConfig; } }; @Before public void setUp() { MockitoAnnotations.initMocks(this); + when(mTetherConfig.isBpfOffloadEnabled()).thenReturn(true /* default value */); } private void waitForIdle() { @@ -501,4 +502,61 @@ public class BpfCoordinatorTest { .addEntry(buildTestEntry(STATS_PER_UID, ethIface, 10, 20, 30, 40)) .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 50, 60, 70, 80))); } + + @Test + public void testTetheringConfigDisable() throws Exception { + setupFunctioningNetdInterface(); + when(mTetherConfig.isBpfOffloadEnabled()).thenReturn(false); + + final BpfCoordinator coordinator = makeBpfCoordinator(); + coordinator.startPolling(); + + // The tether stats polling task should not be scheduled. + mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS); + waitForIdle(); + verify(mNetd, never()).tetherOffloadGetStats(); + + // The interface name lookup table can't be added. + final String iface = "rmnet_data0"; + final Integer ifIndex = 100; + coordinator.addUpstreamNameToLookupTable(ifIndex, iface); + assertEquals(0, coordinator.getInterfaceNamesForTesting().size()); + + // The rule can't be added. + final InetAddress neigh = InetAddresses.parseNumericAddress("2001:db8::1"); + final MacAddress mac = MacAddress.fromString("00:00:00:00:00:0a"); + final Ipv6ForwardingRule rule = buildTestForwardingRule(ifIndex, neigh, mac); + coordinator.tetherOffloadRuleAdd(mIpServer, rule); + verify(mNetd, never()).tetherOffloadRuleAdd(any()); + LinkedHashMap rules = + coordinator.getForwardingRulesForTesting().get(mIpServer); + assertNull(rules); + + // The rule can't be removed. This is not a realistic case because adding rule is not + // allowed. That implies no rule could be removed, cleared or updated. Verify these + // cases just in case. + rules = new LinkedHashMap(); + rules.put(rule.address, rule); + coordinator.getForwardingRulesForTesting().put(mIpServer, rules); + coordinator.tetherOffloadRuleRemove(mIpServer, rule); + verify(mNetd, never()).tetherOffloadRuleRemove(any()); + rules = coordinator.getForwardingRulesForTesting().get(mIpServer); + assertNotNull(rules); + assertEquals(1, rules.size()); + + // The rule can't be cleared. + coordinator.tetherOffloadRuleClear(mIpServer); + verify(mNetd, never()).tetherOffloadRuleRemove(any()); + rules = coordinator.getForwardingRulesForTesting().get(mIpServer); + assertNotNull(rules); + assertEquals(1, rules.size()); + + // The rule can't be updated. + coordinator.tetherOffloadRuleUpdate(mIpServer, rule.upstreamIfindex + 1 /* new */); + verify(mNetd, never()).tetherOffloadRuleRemove(any()); + verify(mNetd, never()).tetherOffloadRuleAdd(any()); + rules = coordinator.getForwardingRulesForTesting().get(mIpServer); + assertNotNull(rules); + assertEquals(1, rules.size()); + } } diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java index 312186391d..a9ac4e2851 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java @@ -294,7 +294,7 @@ public class TetheringConfigurationTest { initializeBpfOffloadConfiguration(true, null /* unset */); final TetheringConfiguration enableByRes = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - assertTrue(enableByRes.enableBpfOffload); + assertTrue(enableByRes.isBpfOffloadEnabled()); } @Test @@ -303,7 +303,7 @@ public class TetheringConfigurationTest { initializeBpfOffloadConfiguration(res, "true"); final TetheringConfiguration enableByDevConOverride = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - assertTrue(enableByDevConOverride.enableBpfOffload); + assertTrue(enableByDevConOverride.isBpfOffloadEnabled()); } } @@ -312,7 +312,7 @@ public class TetheringConfigurationTest { initializeBpfOffloadConfiguration(false, null /* unset */); final TetheringConfiguration disableByRes = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - assertFalse(disableByRes.enableBpfOffload); + assertFalse(disableByRes.isBpfOffloadEnabled()); } @Test @@ -321,7 +321,7 @@ public class TetheringConfigurationTest { initializeBpfOffloadConfiguration(res, "false"); final TetheringConfiguration disableByDevConOverride = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - assertFalse(disableByDevConOverride.enableBpfOffload); + assertFalse(disableByDevConOverride.isBpfOffloadEnabled()); } } From 1c0d8487b48115eba8eb87ed84c1673b5188f028 Mon Sep 17 00:00:00 2001 From: Nucca Chen Date: Wed, 17 Jun 2020 07:04:26 +0000 Subject: [PATCH 174/188] [BOT.13] Make offload coordinator poll interval configurable Bug: 150736748 Test: BpfCoordinatorTest Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1315576 Merged-In: I7f8dde3b57ee14eb33edbe2fd383df33cccc231c Change-Id: I70a6e5c8e765daf40cb738feb7fd70cf3c8f052d (cherry picked from commit bb61893406993556386fa2b8894bad4e3b14bd4e) --- .../tethering/BpfCoordinator.java | 26 ++++--- .../tethering/BpfCoordinatorTest.java | 71 +++++++++++++++---- 2 files changed, 73 insertions(+), 24 deletions(-) diff --git a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java index 12464e1289..20f30ea7a4 100644 --- a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java +++ b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java @@ -25,6 +25,8 @@ import static android.net.NetworkStats.UID_ALL; import static android.net.NetworkStats.UID_TETHERING; import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED; +import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS; + import android.app.usage.NetworkStatsManager; import android.net.INetd; import android.net.MacAddress; @@ -69,8 +71,6 @@ import java.util.Objects; public class BpfCoordinator { private static final String TAG = BpfCoordinator.class.getSimpleName(); private static final int DUMP_TIMEOUT_MS = 10_000; - @VisibleForTesting - static final int DEFAULT_PERFORM_POLL_INTERVAL_MS = 5000; // TODO: Make it customizable. @VisibleForTesting enum StatsType { @@ -154,14 +154,6 @@ public class BpfCoordinator { @VisibleForTesting public abstract static class Dependencies { - /** - * Get polling Interval in milliseconds. - */ - public int getPerformPollInterval() { - // TODO: Consider make this configurable. - return DEFAULT_PERFORM_POLL_INTERVAL_MS; - } - /** Get handler. */ @NonNull public abstract Handler getHandler(); @@ -403,6 +395,7 @@ public class BpfCoordinator { pw.println("Stats provider " + (mStatsProvider != null ? "registered" : "not registered")); pw.println("Upstream quota: " + mInterfaceQuotas.toString()); + pw.println("Polling interval: " + getPollingInterval() + " ms"); pw.println("Forwarding stats:"); pw.increaseIndent(); @@ -745,6 +738,17 @@ public class BpfCoordinator { updateQuotaAndStatsFromSnapshot(tetherStatsList); } + @VisibleForTesting + int getPollingInterval() { + // The valid range of interval is DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS..max_long. + // Ignore the config value is less than the minimum polling interval. Note that the + // minimum interval definition is invoked as OffloadController#isPollingStatsNeeded does. + // TODO: Perhaps define a minimum polling interval constant. + final TetheringConfiguration config = mDeps.getTetherConfig(); + final int configInterval = (config != null) ? config.getOffloadPollInterval() : 0; + return Math.max(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS, configInterval); + } + private void maybeSchedulePollingStats() { if (!mPollingStarted) return; @@ -752,7 +756,7 @@ public class BpfCoordinator { mHandler.removeCallbacks(mScheduledPollingTask); } - mHandler.postDelayed(mScheduledPollingTask, mDeps.getPerformPollInterval()); + mHandler.postDelayed(mScheduledPollingTask, getPollingInterval()); } // Return forwarding rule map. This is used for testing only. diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java index 31d98147c7..64242ae825 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java @@ -25,11 +25,10 @@ import static android.net.NetworkStats.UID_ALL; import static android.net.NetworkStats.UID_TETHERING; import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED; -import static com.android.networkstack.tethering.BpfCoordinator - .DEFAULT_PERFORM_POLL_INTERVAL_MS; import static com.android.networkstack.tethering.BpfCoordinator.StatsType; import static com.android.networkstack.tethering.BpfCoordinator.StatsType.STATS_PER_IFACE; import static com.android.networkstack.tethering.BpfCoordinator.StatsType.STATS_PER_UID; +import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -104,11 +103,6 @@ public class BpfCoordinatorTest { private final TestLooper mTestLooper = new TestLooper(); private BpfCoordinator.Dependencies mDeps = new BpfCoordinator.Dependencies() { - @Override - public int getPerformPollInterval() { - return DEFAULT_PERFORM_POLL_INTERVAL_MS; - } - @NonNull public Handler getHandler() { return new Handler(mTestLooper.getLooper()); @@ -183,9 +177,11 @@ public class BpfCoordinatorTest { return parcel; } + // Set up specific tether stats list and wait for the stats cache is updated by polling thread + // in the coordinator. Beware of that it is only used for the default polling interval. private void setTetherOffloadStatsList(TetherStatsParcel[] tetherStatsList) throws Exception { when(mNetd.tetherOffloadGetStats()).thenReturn(tetherStatsList); - mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS); + mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); waitForIdle(); } @@ -254,7 +250,7 @@ public class BpfCoordinatorTest { clearInvocations(mNetd); // Verify the polling update thread stopped. - mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS); + mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); waitForIdle(); verify(mNetd, never()).tetherOffloadGetStats(); } @@ -279,20 +275,20 @@ public class BpfCoordinatorTest { when(mNetd.tetherOffloadGetStats()).thenReturn( new TetherStatsParcel[] {buildTestTetherStatsParcel(mobileIfIndex, 0, 0, 0, 0)}); mTetherStatsProvider.onSetAlert(100); - mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS); + mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); waitForIdle(); mTetherStatsProviderCb.assertNoCallback(); // Verify that notifyAlertReached fired when quota is reached. when(mNetd.tetherOffloadGetStats()).thenReturn( new TetherStatsParcel[] {buildTestTetherStatsParcel(mobileIfIndex, 50, 0, 50, 0)}); - mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS); + mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); waitForIdle(); mTetherStatsProviderCb.expectNotifyAlertReached(); // Verify that set quota with UNLIMITED won't trigger any callback. mTetherStatsProvider.onSetAlert(QUOTA_UNLIMITED); - mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS); + mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); waitForIdle(); mTetherStatsProviderCb.assertNoCallback(); } @@ -512,7 +508,7 @@ public class BpfCoordinatorTest { coordinator.startPolling(); // The tether stats polling task should not be scheduled. - mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS); + mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); waitForIdle(); verify(mNetd, never()).tetherOffloadGetStats(); @@ -559,4 +555,53 @@ public class BpfCoordinatorTest { assertNotNull(rules); assertEquals(1, rules.size()); } + + @Test + public void testTetheringConfigSetPollingInterval() throws Exception { + setupFunctioningNetdInterface(); + + final BpfCoordinator coordinator = makeBpfCoordinator(); + + // [1] The default polling interval. + coordinator.startPolling(); + assertEquals(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS, coordinator.getPollingInterval()); + coordinator.stopPolling(); + + // [2] Expect the invalid polling interval isn't applied. The valid range of interval is + // DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS..max_long. + for (final int interval + : new int[] {0, 100, DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS - 1}) { + when(mTetherConfig.getOffloadPollInterval()).thenReturn(interval); + coordinator.startPolling(); + assertEquals(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS, coordinator.getPollingInterval()); + coordinator.stopPolling(); + } + + // [3] Set a specific polling interval which is larger than default value. + // Use a large polling interval to avoid flaky test because the time forwarding + // approximation is used to verify the scheduled time of the polling thread. + final int pollingInterval = 100_000; + when(mTetherConfig.getOffloadPollInterval()).thenReturn(pollingInterval); + coordinator.startPolling(); + + // Expect the specific polling interval to be applied. + assertEquals(pollingInterval, coordinator.getPollingInterval()); + + // Start on a new polling time slot. + mTestLooper.moveTimeForward(pollingInterval); + waitForIdle(); + clearInvocations(mNetd); + + // Move time forward to 90% polling interval time. Expect that the polling thread has not + // scheduled yet. + mTestLooper.moveTimeForward((long) (pollingInterval * 0.9)); + waitForIdle(); + verify(mNetd, never()).tetherOffloadGetStats(); + + // Move time forward to the remaining 10% polling interval time. Expect that the polling + // thread has scheduled. + mTestLooper.moveTimeForward((long) (pollingInterval * 0.1)); + waitForIdle(); + verify(mNetd).tetherOffloadGetStats(); + } } From 8dd7e0a936c8e499159cc2b0ac67a3785f36f4ec Mon Sep 17 00:00:00 2001 From: Luke Huang Date: Tue, 16 Jun 2020 08:13:28 +0800 Subject: [PATCH 175/188] Move DnsPacket to libs net This class might be used by some mainline modules. Bug: 151052811 Test: atest DnsPacketTest Test: atest DnsResolverTest Change-Id: I8841d91456952ded5efbf8ea221289aecc7746ad --- Tethering/jarjar-rules.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Tethering/jarjar-rules.txt b/Tethering/jarjar-rules.txt index e90a2ccaa2..8f072e4cd2 100644 --- a/Tethering/jarjar-rules.txt +++ b/Tethering/jarjar-rules.txt @@ -15,3 +15,6 @@ rule com.android.internal.util.TrafficStatsConstants* com.android.networkstack.t rule android.net.LocalLog* com.android.networkstack.tethering.LocalLog@1 rule android.net.shared.Inet4AddressUtils* com.android.networkstack.tethering.shared.Inet4AddressUtils@1 + +# Classes from net-utils-framework-common +rule com.android.net.module.util.** com.android.networkstack.tethering.util.@1 \ No newline at end of file From 93e59ee7260eb2fdd89e89e8eea8e63da73f3bb5 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Thu, 18 Jun 2020 16:36:32 +0000 Subject: [PATCH 176/188] Memory leak due to no stop for IpNeighborMonitor. 1. When Wi-Fi enabled, ap0 interface is added and IpNeighborMonitor's start() is invoked in IpServer's constructor. 2. There's no stop for IpNeighborMonitor when IpServer stop. 3. During overnight test for Wi-Fi, networkstack is too slow to process request due to memory leak in IpNeighborMonitor and binder buffer is blocked causing exception. Solution: Invoke IpNeighborMonitor.stop() in UnavailableState.enter(). Bug: 159097215 Test: automatic, turn on and off Wi-Fi every 6 seconds overnight Original-Change: https://android-review.googlesource.com/1343440 Merged-In: I8f60c13706f05306e8f25a15f7861d7ecabbc10e Change-Id: I8f60c13706f05306e8f25a15f7861d7ecabbc10e --- Tethering/src/android/net/ip/IpServer.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java index 5239947403..c436be2e9b 100644 --- a/Tethering/src/android/net/ip/IpServer.java +++ b/Tethering/src/android/net/ip/IpServer.java @@ -1322,6 +1322,7 @@ public class IpServer extends StateMachine { class UnavailableState extends State { @Override public void enter() { + mIpNeighborMonitor.stop(); mLastError = TetheringManager.TETHER_ERROR_NO_ERROR; sendInterfaceState(STATE_UNAVAILABLE); } From 9644bc01dddfe5d801ca722a3f9fcdda4036e420 Mon Sep 17 00:00:00 2001 From: Treehugger Robot Date: Fri, 19 Jun 2020 03:04:46 +0000 Subject: [PATCH 177/188] Test that IpNeighborMonitor is stopped when IpServer stops. Bug: 159097215 Test: test-only change Original-Change: https://android-review.googlesource.com/1343441 Merged-In: I2292c1cbff06a304f70191b88d833b19af2b8b92 Change-Id: I2292c1cbff06a304f70191b88d833b19af2b8b92 --- Tethering/tests/unit/src/android/net/ip/IpServerTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java index c65069c544..30a9d2252e 100644 --- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java +++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java @@ -860,6 +860,7 @@ public class IpServerTest { verify(mBpfCoordinator).tetherOffloadRuleClear(mIpServer); verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macA)); verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB)); + verify(mIpNeighborMonitor).stop(); resetNetdAndBpfCoordinator(); } From a4a8a315fe10fb65cc231582fcd22f8912786995 Mon Sep 17 00:00:00 2001 From: Treehugger Robot Date: Thu, 18 Jun 2020 06:45:55 +0000 Subject: [PATCH 178/188] Add owneship of tethering module Bug: 158961959 Test: build/make/tools/checkowners.py packages/NetworkStack/OWNERS Test: ./build/make/tools/checkowners.py \ frameworks/base/packages/Tethering/OWNERS Merged-In: I13b291e5db6a8a8c9f2bfa477acabaea250aa48c Change-Id: I13b291e5db6a8a8c9f2bfa477acabaea250aa48c --- Tethering/OWNERS | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Tethering/OWNERS diff --git a/Tethering/OWNERS b/Tethering/OWNERS new file mode 100644 index 0000000000..5b42d49041 --- /dev/null +++ b/Tethering/OWNERS @@ -0,0 +1,2 @@ +include platform/packages/modules/NetworkStack/:/OWNERS +markchien@google.com From 675697222aa441cc2cc0b8e65182eb5d7c5f28d5 Mon Sep 17 00:00:00 2001 From: Mark Chien Date: Fri, 19 Jun 2020 06:25:12 +0000 Subject: [PATCH 179/188] Let only Ethernet manage ethernet interface up and down When LAN link is changed for using ethernet interface from Ethernet tethering to Ethernet, there is confiict as Ethernet Tethering is trying to make ethernet link down and Ethernet is trying to make ethernet link up. So, this would make Ethernet only manage ethernet interface link state. Test: manual Bug: 130840861 Merged-In: I1cd40ae764bdeecbb59e3889e9399b7f4b05f9cc Change-Id: I1cd40ae764bdeecbb59e3889e9399b7f4b05f9cc --- Tethering/src/android/net/ip/IpServer.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java index c436be2e9b..35c156304c 100644 --- a/Tethering/src/android/net/ip/IpServer.java +++ b/Tethering/src/android/net/ip/IpServer.java @@ -616,8 +616,9 @@ public class IpServer extends StateMachine { final Boolean setIfaceUp; if (mInterfaceType == TetheringManager.TETHERING_WIFI - || mInterfaceType == TetheringManager.TETHERING_WIFI_P2P) { - // The WiFi stack has ownership of the interface up/down state. + || mInterfaceType == TetheringManager.TETHERING_WIFI_P2P + || mInterfaceType == TetheringManager.TETHERING_ETHERNET) { + // The WiFi and Ethernet stack has ownership of the interface up/down state. // It is unclear whether the Bluetooth or USB stacks will manage their own // state. setIfaceUp = null; From b7048af0a952d8ef92af440c54a7adb06c2068ea Mon Sep 17 00:00:00 2001 From: waynema Date: Fri, 19 Jun 2020 19:48:56 +0800 Subject: [PATCH 180/188] Add framework-statsd dependency and make framework-statsd visible to Tethering package. Tethering needs StatsEvent and StatsLog which are in framework-statsd.stubs.module_lib to write metrics. Bug: 153942334 Test: make Change-Id: I8ba142a9b3678de36e6c4880a64e992e00680f38 --- Tethering/Android.bp | 1 + 1 file changed, 1 insertion(+) diff --git a/Tethering/Android.bp b/Tethering/Android.bp index 32e2b04e8e..12daa6142d 100644 --- a/Tethering/Android.bp +++ b/Tethering/Android.bp @@ -33,6 +33,7 @@ java_defaults { "net-utils-framework-common", ], libs: [ + "framework-statsd.stubs.module_lib", "framework-tethering.impl", "framework-wifi", "unsupportedappusage", From 4dd87aafc0062d455d5bf0aee7169d2c6b3b3039 Mon Sep 17 00:00:00 2001 From: Dedy Lansky Date: Thu, 21 Nov 2019 00:36:14 +0200 Subject: [PATCH 181/188] Tethering: Add WiGig support This change is a combination of following changes: 1) Tethering: add TETHERING_WIGIG type Currently both WIFI and WIGIG use the same tethering type, TETHERING_WIFI. This causes conflicts between the frameworks, when both WIFI and WIGIG SoftAPs are started, one or both will not work. Fix this by using a seperate tethering type for WIGIG. 2) Tethering: remove TETHERING_WIGIG state machine on interface down The wigig state machine relies on a TETHERING_STATE_CHANGED broadcast that is sent when the tethering state machine is first created, during interface up. Currently the tethering state machine is not removed on interface down except for TETHERING_BLUETOOTH, and as a result wigig tethering only works the first time SoftAP is started. In order to fix this, remove the tethering state machine on interface down for TETHERING_WIGIG as well. Bug: 143356416 Test: TetheringCoverageTests Change-Id: Ic4d3aca0ed69234093af7f0206dab3335938c52a Merged-In: Ic4d3aca0ed69234093af7f0206dab3335938c52a --- .../TetheringLib/src/android/net/TetheringManager.java | 8 ++++++++ Tethering/res/values/config.xml | 7 +++++++ Tethering/res/values/overlayable.xml | 1 + Tethering/src/android/net/ip/IpServer.java | 3 ++- .../src/com/android/networkstack/tethering/Tethering.java | 6 +++++- .../networkstack/tethering/TetheringConfiguration.java | 7 +++++++ 6 files changed, 30 insertions(+), 2 deletions(-) diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java index cc095a0bb4..4f053cb65c 100644 --- a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java +++ b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java @@ -171,6 +171,14 @@ public class TetheringManager { */ public static final int TETHERING_ETHERNET = 5; + /** + * WIGIG tethering type. Use a separate type to prevent + * conflicts with TETHERING_WIFI + * This type is only used internally by the tethering module + * @hide + */ + public static final int TETHERING_WIGIG = 6; + /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(value = { diff --git a/Tethering/res/values/config.xml b/Tethering/res/values/config.xml index 9269c6f0fd..9b9dcde910 100644 --- a/Tethering/res/values/config.xml +++ b/Tethering/res/values/config.xml @@ -42,6 +42,13 @@ "softap\\d" + + + "wigig\\d" + + diff --git a/Tethering/res/values/overlayable.xml b/Tethering/res/values/overlayable.xml index 4e2bb1e31b..6a33d55cb0 100644 --- a/Tethering/res/values/overlayable.xml +++ b/Tethering/res/values/overlayable.xml @@ -20,6 +20,7 @@ + diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java index 35c156304c..8af1797a9d 100644 --- a/Tethering/src/android/net/ip/IpServer.java +++ b/Tethering/src/android/net/ip/IpServer.java @@ -617,7 +617,8 @@ public class IpServer extends StateMachine { final Boolean setIfaceUp; if (mInterfaceType == TetheringManager.TETHERING_WIFI || mInterfaceType == TetheringManager.TETHERING_WIFI_P2P - || mInterfaceType == TetheringManager.TETHERING_ETHERNET) { + || mInterfaceType == TetheringManager.TETHERING_ETHERNET + || mInterfaceType == TetheringManager.TETHERING_WIGIG) { // The WiFi and Ethernet stack has ownership of the interface up/down state. // It is unclear whether the Bluetooth or USB stacks will manage their own // state. diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java index c72ac52740..3695ec65d5 100644 --- a/Tethering/src/com/android/networkstack/tethering/Tethering.java +++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java @@ -40,6 +40,7 @@ import static android.net.TetheringManager.TETHERING_NCM; import static android.net.TetheringManager.TETHERING_USB; import static android.net.TetheringManager.TETHERING_WIFI; import static android.net.TetheringManager.TETHERING_WIFI_P2P; +import static android.net.TetheringManager.TETHERING_WIGIG; import static android.net.TetheringManager.TETHER_ERROR_INTERNAL_ERROR; import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; import static android.net.TetheringManager.TETHER_ERROR_SERVICE_UNAVAIL; @@ -495,7 +496,8 @@ public class Tethering { if (up) { maybeTrackNewInterfaceLocked(iface); } else { - if (ifaceNameToType(iface) == TETHERING_BLUETOOTH) { + if (ifaceNameToType(iface) == TETHERING_BLUETOOTH + || ifaceNameToType(iface) == TETHERING_WIGIG) { stopTrackingInterfaceLocked(iface); } else { // Ignore usb0 down after enabling RNDIS. @@ -517,6 +519,8 @@ public class Tethering { if (cfg.isWifi(iface)) { return TETHERING_WIFI; + } else if (cfg.isWigig(iface)) { + return TETHERING_WIGIG; } else if (cfg.isWifiP2p(iface)) { return TETHERING_WIFI_P2P; } else if (cfg.isUsb(iface)) { diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java index 18b2b7804f..e1771a5613 100644 --- a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java @@ -92,6 +92,7 @@ public class TetheringConfiguration { public final String[] tetherableUsbRegexs; public final String[] tetherableWifiRegexs; + public final String[] tetherableWigigRegexs; public final String[] tetherableWifiP2pRegexs; public final String[] tetherableBluetoothRegexs; public final String[] tetherableNcmRegexs; @@ -125,6 +126,7 @@ public class TetheringConfiguration { // us an interface name. Careful consideration needs to be given to // implications for Settings and for provisioning checks. tetherableWifiRegexs = getResourceStringArray(res, R.array.config_tether_wifi_regexs); + tetherableWigigRegexs = getResourceStringArray(res, R.array.config_tether_wigig_regexs); tetherableWifiP2pRegexs = getResourceStringArray( res, R.array.config_tether_wifi_p2p_regexs); tetherableBluetoothRegexs = getResourceStringArray( @@ -167,6 +169,11 @@ public class TetheringConfiguration { return matchesDownstreamRegexs(iface, tetherableWifiRegexs); } + /** Check whether input interface belong to wigig.*/ + public boolean isWigig(String iface) { + return matchesDownstreamRegexs(iface, tetherableWigigRegexs); + } + /** Check whether this interface is Wifi P2P interface. */ public boolean isWifiP2p(String iface) { return matchesDownstreamRegexs(iface, tetherableWifiP2pRegexs); From e8141aa9c3f4f2adc744fd6605d783a86938f834 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Wed, 24 Jun 2020 03:44:31 +0000 Subject: [PATCH 182/188] tethering: offload: Netlink Req Send netlink request over fd for offload config before completing init sequence. Provides existing conntrack entries to IPA. Resolves issue where there are NAT misses in IPA due to IPA only having the conntrack entries added after tethering starts. Bug: 149109043 Test: OffloadHardwareInterfaceTest Original-Change: https://android-review.googlesource.com/1290954 Merged-In: Iaf3e847e92f205b55f10fa85c17b9f3995d52099 Change-Id: Iaf3e847e92f205b55f10fa85c17b9f3995d52099 --- .../tethering/OffloadHardwareInterface.java | 33 ++++++++++++++++ .../OffloadHardwareInterfaceTest.java | 39 +++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java b/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java index fe92204c25..33b9d00e70 100644 --- a/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java +++ b/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java @@ -16,8 +16,11 @@ package com.android.networkstack.tethering; +import static android.net.netlink.StructNlMsgHdr.NLM_F_DUMP; +import static android.net.netlink.StructNlMsgHdr.NLM_F_REQUEST; import static android.net.util.TetheringUtils.uint16; +import android.annotation.NonNull; import android.hardware.tetheroffload.config.V1_0.IOffloadConfig; import android.hardware.tetheroffload.control.V1_0.IOffloadControl; import android.hardware.tetheroffload.control.V1_0.ITetheringOffloadCallback; @@ -25,6 +28,7 @@ import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate; import android.hardware.tetheroffload.control.V1_0.NetworkProtocol; import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent; import android.net.netlink.NetlinkSocket; +import android.net.netlink.StructNlMsgHdr; import android.net.util.SharedLog; import android.net.util.SocketUtils; import android.os.Handler; @@ -37,9 +41,11 @@ import android.system.OsConstants; import com.android.internal.annotations.VisibleForTesting; import java.io.FileDescriptor; +import java.io.InterruptedIOException; import java.io.IOException; import java.net.SocketAddress; import java.net.SocketException; +import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.NoSuchElementException; @@ -63,6 +69,11 @@ public class OffloadHardwareInterface { private static final int NF_NETLINK_CONNTRACK_NEW = 1; private static final int NF_NETLINK_CONNTRACK_UPDATE = 2; private static final int NF_NETLINK_CONNTRACK_DESTROY = 4; + // Reference libnetfilter_conntrack/linux_nfnetlink_conntrack.h + public static final short NFNL_SUBSYS_CTNETLINK = 1; + public static final short IPCTNL_MSG_CT_GET = 1; + + private final long NETLINK_MESSAGE_TIMEOUT_MS = 500; private final Handler mHandler; private final SharedLog mLog; @@ -226,6 +237,9 @@ public class OffloadHardwareInterface { NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY); if (h1 == null) return false; + sendNetlinkMessage(h1, (short) ((NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_GET), + (short) (NLM_F_REQUEST | NLM_F_DUMP)); + final NativeHandle h2 = mDeps.createConntrackSocket( NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY); if (h2 == null) { @@ -252,6 +266,25 @@ public class OffloadHardwareInterface { return results.mSuccess; } + @VisibleForTesting + public void sendNetlinkMessage(@NonNull NativeHandle handle, short type, short flags) { + final int length = StructNlMsgHdr.STRUCT_SIZE; + final byte[] msg = new byte[length]; + final StructNlMsgHdr nlh = new StructNlMsgHdr(); + final ByteBuffer byteBuffer = ByteBuffer.wrap(msg); + nlh.nlmsg_len = length; + nlh.nlmsg_type = type; + nlh.nlmsg_flags = flags; + nlh.nlmsg_seq = 1; + nlh.pack(byteBuffer); + try { + NetlinkSocket.sendMessage(handle.getFileDescriptor(), msg, 0 /* offset */, length, + NETLINK_MESSAGE_TIMEOUT_MS); + } catch (ErrnoException | InterruptedIOException e) { + mLog.e("Unable to send netfilter message, error: " + e); + } + } + private void closeFdInNativeHandle(final NativeHandle h) { try { h.close(); diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java index f8ff1cb29c..c543fad62d 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java @@ -17,13 +17,17 @@ package com.android.networkstack.tethering; import static android.net.util.TetheringUtils.uint16; +import static android.system.OsConstants.SOCK_STREAM; +import static android.system.OsConstants.AF_UNIX; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.hardware.tetheroffload.config.V1_0.IOffloadConfig; import android.hardware.tetheroffload.control.V1_0.IOffloadControl; @@ -31,11 +35,14 @@ import android.hardware.tetheroffload.control.V1_0.ITetheringOffloadCallback; import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate; import android.hardware.tetheroffload.control.V1_0.NetworkProtocol; import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent; +import android.net.netlink.StructNlMsgHdr; import android.net.util.SharedLog; import android.os.Handler; import android.os.NativeHandle; import android.os.test.TestLooper; +import android.system.ErrnoException; import android.system.OsConstants; +import android.system.Os; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -47,6 +54,9 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.io.FileDescriptor; +import java.io.OutputStream; +import java.nio.ByteBuffer; import java.util.ArrayList; @RunWith(AndroidJUnit4.class) @@ -64,6 +74,10 @@ public final class OffloadHardwareInterfaceTest { @Mock private IOffloadControl mIOffloadControl; @Mock private NativeHandle mNativeHandle; + // Random values to test Netlink message. + private static final short TEST_TYPE = 184; + private static final short TEST_FLAGS = 263; + class MyDependencies extends OffloadHardwareInterface.Dependencies { MyDependencies(SharedLog log) { super(log); @@ -203,6 +217,31 @@ public final class OffloadHardwareInterfaceTest { eq(uint16(udpParams.dst.port))); } + @Test + public void testNetlinkMessage() throws Exception { + FileDescriptor writeSocket = new FileDescriptor(); + FileDescriptor readSocket = new FileDescriptor(); + try { + Os.socketpair(AF_UNIX, SOCK_STREAM, 0, writeSocket, readSocket); + } catch (ErrnoException e) { + fail(); + return; + } + when(mNativeHandle.getFileDescriptor()).thenReturn(writeSocket); + + mOffloadHw.sendNetlinkMessage(mNativeHandle, TEST_TYPE, TEST_FLAGS); + + ByteBuffer buffer = ByteBuffer.allocate(StructNlMsgHdr.STRUCT_SIZE); + int read = Os.read(readSocket, buffer); + + buffer.flip(); + assertEquals(StructNlMsgHdr.STRUCT_SIZE, buffer.getInt()); + assertEquals(TEST_TYPE, buffer.getShort()); + assertEquals(TEST_FLAGS, buffer.getShort()); + assertEquals(1 /* seq */, buffer.getInt()); + assertEquals(0 /* pid */, buffer.getInt()); + } + private NatTimeoutUpdate buildNatTimeoutUpdate(final int proto) { final NatTimeoutUpdate params = new NatTimeoutUpdate(); params.proto = proto; From 697ec868d2478aa6ea7249f756ad4f7f6fe75d64 Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Thu, 11 Jun 2020 16:37:31 +0100 Subject: [PATCH 183/188] Sync with libnativehelper refactoring jniGetFDFromFileDescriptor() is now a legacy method and moved to a separate header. Bug: 151443957 Bug: 158749603 Test: m Change-Id: Icd06e9a315680c2251dbb9032a904dd6d66aa359 Merged-In: Icd06e9a315680c2251dbb9032a904dd6d66aa359 Exempt-From-Owner-Approval: cherry pick (cherry picked from commit a5c4682505d0336113654bb7f5ea77b9d8dad626) --- Tethering/jni/android_net_util_TetheringUtils.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Tethering/jni/android_net_util_TetheringUtils.cpp b/Tethering/jni/android_net_util_TetheringUtils.cpp index 5493440644..f6eb40a842 100644 --- a/Tethering/jni/android_net_util_TetheringUtils.cpp +++ b/Tethering/jni/android_net_util_TetheringUtils.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include From 683932315b477ac1439d60e5b6e8645a4060e406 Mon Sep 17 00:00:00 2001 From: markchien Date: Tue, 30 Jun 2020 22:56:12 +0800 Subject: [PATCH 184/188] Fix tethering jarjar rule for LocalLog LocalLog is in android.util* instead of android.net*. No crash happened because the methods used by tethering are @UnsupportedAppUsage. Bug: 160113128 Test: atest TetheringTests Change-Id: I5f070b96f96aaabf7ec8da52a3d3444ed940fb56 --- Tethering/jarjar-rules.txt | 2 +- Tethering/tests/unit/jarjar-rules.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Tethering/jarjar-rules.txt b/Tethering/jarjar-rules.txt index 2d3108a0bf..591861f5b8 100644 --- a/Tethering/jarjar-rules.txt +++ b/Tethering/jarjar-rules.txt @@ -3,7 +3,7 @@ # If there are files in that filegroup that are not covered below, the classes in the # module will be overwritten by the ones in the framework. rule com.android.internal.util.** com.android.networkstack.tethering.util.@1 -rule android.net.LocalLog* com.android.networkstack.tethering.LocalLog@1 +rule android.util.LocalLog* com.android.networkstack.tethering.util.LocalLog@1 rule android.net.shared.Inet4AddressUtils* com.android.networkstack.tethering.shared.Inet4AddressUtils@1 diff --git a/Tethering/tests/unit/jarjar-rules.txt b/Tethering/tests/unit/jarjar-rules.txt index 1ea56cdf1a..ec2d2b0200 100644 --- a/Tethering/tests/unit/jarjar-rules.txt +++ b/Tethering/tests/unit/jarjar-rules.txt @@ -8,4 +8,4 @@ rule com.android.internal.util.State* com.android.networkstack.tethering.util.St rule com.android.internal.util.StateMachine* com.android.networkstack.tethering.util.StateMachine@1 rule com.android.internal.util.TrafficStatsConstants* com.android.networkstack.tethering.util.TrafficStatsConstants@1 -rule android.net.LocalLog* com.android.networkstack.tethering.LocalLog@1 +rule android.util.LocalLog* com.android.networkstack.tethering.util.LocalLog@1 From 6faf8619945930f9aef6c21269343d2ea0db57b9 Mon Sep 17 00:00:00 2001 From: Treehugger Robot Date: Wed, 8 Jul 2020 05:06:20 +0000 Subject: [PATCH 185/188] Skip testPhysicalEthernet if the test run adb over network If the test run adb over network and ethernet is available, it is likely that adb may run over ethernet. Then the test would fail because adb would break when ethernet is switching from client mode to server mode. Bug: 160389275 Test: atest CtsTetheringTest Merged-In: I57d365d33316881c50c3bf7fd1c98926d10842d3 Change-Id: I57d365d33316881c50c3bf7fd1c98926d10842d3 --- .../src/android/net/EthernetTetheringTest.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java index 74df11370e..e10bab4b36 100644 --- a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java +++ b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java @@ -42,6 +42,7 @@ import android.net.dhcp.DhcpPacket; import android.os.Handler; import android.os.HandlerThread; import android.os.SystemClock; +import android.os.SystemProperties; import android.system.Os; import android.util.Log; @@ -224,9 +225,19 @@ public class EthernetTetheringTest { } + private boolean isAdbOverNetwork() { + // If adb TCP port opened, this test may running by adb over network. + return (SystemProperties.getInt("persist.adb.tcp.port", -1) > -1) + || (SystemProperties.getInt("service.adb.tcp.port", -1) > -1); + } + @Test public void testPhysicalEthernet() throws Exception { assumeTrue(mEm.isAvailable()); + // Do not run this test if adb is over network and ethernet is connected. + // It is likely the adb run over ethernet, the adb would break when ethernet is switching + // from client mode to server mode. See b/160389275. + assumeFalse(isAdbOverNetwork()); // Get an interface to use. final String iface = mTetheredInterfaceRequester.getInterface(); From 0275981699b68dba728870112ca9253714e8dbc0 Mon Sep 17 00:00:00 2001 From: Treehugger Robot Date: Mon, 13 Jul 2020 09:35:07 +0000 Subject: [PATCH 186/188] Fix EthernetTetheringTest failure if tethering is not supported Do not run ethernet tethering tests if tethering is not supported because Tethering APIs would fail. Also skip the test if EthernetManager is not avaliable. EthernetManager would be null if there are no FEATURE_ETHERNET and FEATURE_USB_HOST. Bug: 159869957 Test: atest CtsTetheringTest Merged-In: I2b9be6799c6edeefc8cd74897a8704dbe80dd061 Change-Id: I2b9be6799c6edeefc8cd74897a8704dbe80dd061 --- .../src/android/net/EthernetTetheringTest.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java index e10bab4b36..9bb01ae5df 100644 --- a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java +++ b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java @@ -102,17 +102,21 @@ public class EthernetTetheringTest { private UiAutomation mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation(); + private boolean mRunTests; @Before public void setUp() throws Exception { - mHandlerThread = new HandlerThread(getClass().getSimpleName()); - mHandlerThread.start(); - mHandler = new Handler(mHandlerThread.getLooper()); - mTetheredInterfaceRequester = new TetheredInterfaceRequester(mHandler, mEm); // Needed to create a TestNetworkInterface, to call requestTetheredInterface, and to receive // tethered client callbacks. mUiAutomation.adoptShellPermissionIdentity( MANAGE_TEST_NETWORKS, NETWORK_SETTINGS, TETHER_PRIVILEGED); + mRunTests = mTm.isTetheringSupported() && mEm != null; + assumeTrue(mRunTests); + + mHandlerThread = new HandlerThread(getClass().getSimpleName()); + mHandlerThread.start(); + mHandler = new Handler(mHandlerThread.getLooper()); + mTetheredInterfaceRequester = new TetheredInterfaceRequester(mHandler, mEm); } private void cleanUp() throws Exception { @@ -136,7 +140,7 @@ public class EthernetTetheringTest { @After public void tearDown() throws Exception { try { - cleanUp(); + if (mRunTests) cleanUp(); } finally { mUiAutomation.dropShellPermissionIdentity(); } From ac8f65a5443e5315d03d25ce7635966a5be8ed1d Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Thu, 3 Sep 2020 08:32:04 +0000 Subject: [PATCH 187/188] Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I946ba0e5ca40f934bdc7588eb244969103813c4c --- Tethering/res/values-af/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-am/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-ar/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-as/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-az/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-b+sr+Latn/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-be/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-bg/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-bn/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-bs/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-ca/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-cs/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-da/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-de/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-el/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-en-rAU/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-en-rCA/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-en-rGB/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-en-rIN/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-en-rXC/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-es-rUS/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-es/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-et/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-eu/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-fa/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-fi/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-fr-rCA/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-fr/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-gl/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-gu/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-hi/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-hr/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-hu/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-hy/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-in/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-is/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-it/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-iw/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-ja/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-ka/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-kk/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-km/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-kn/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-ko/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-ky/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-lo/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-lt/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-lv/strings.xml | 29 ++++++++++++++++--- .../res/values-mcc310-mnc004-af/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-am/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-ar/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-as/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-az/strings.xml | 24 +++++++++++++++ .../strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-be/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-bg/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-bn/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-bs/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-ca/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-cs/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-da/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-de/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-el/strings.xml | 24 +++++++++++++++ .../values-mcc310-mnc004-en-rAU/strings.xml | 24 +++++++++++++++ .../values-mcc310-mnc004-en-rCA/strings.xml | 24 +++++++++++++++ .../values-mcc310-mnc004-en-rGB/strings.xml | 24 +++++++++++++++ .../values-mcc310-mnc004-en-rIN/strings.xml | 24 +++++++++++++++ .../values-mcc310-mnc004-en-rXC/strings.xml | 24 +++++++++++++++ .../values-mcc310-mnc004-es-rUS/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-es/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-et/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-eu/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-fa/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-fi/strings.xml | 24 +++++++++++++++ .../values-mcc310-mnc004-fr-rCA/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-fr/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-gl/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-gu/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-hi/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-hr/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-hu/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-hy/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-in/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-is/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-it/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-iw/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-ja/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-ka/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-kk/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-km/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-kn/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-ko/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-ky/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-lo/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-lt/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-lv/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-mk/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-ml/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-mn/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-mr/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-ms/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-my/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-nb/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-ne/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-nl/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-or/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-pa/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-pl/strings.xml | 24 +++++++++++++++ .../values-mcc310-mnc004-pt-rBR/strings.xml | 24 +++++++++++++++ .../values-mcc310-mnc004-pt-rPT/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-pt/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-ro/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-ru/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-si/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-sk/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-sl/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-sq/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-sr/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-sv/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-sw/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-ta/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-te/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-th/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-tl/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-tr/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-uk/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-ur/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-uz/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-vi/strings.xml | 24 +++++++++++++++ .../values-mcc310-mnc004-zh-rCN/strings.xml | 24 +++++++++++++++ .../values-mcc310-mnc004-zh-rHK/strings.xml | 24 +++++++++++++++ .../values-mcc310-mnc004-zh-rTW/strings.xml | 24 +++++++++++++++ .../res/values-mcc310-mnc004-zu/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-af/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-am/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-ar/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-as/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-az/strings.xml | 24 +++++++++++++++ .../strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-be/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-bg/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-bn/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-bs/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-ca/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-cs/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-da/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-de/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-el/strings.xml | 24 +++++++++++++++ .../values-mcc311-mnc480-en-rAU/strings.xml | 24 +++++++++++++++ .../values-mcc311-mnc480-en-rCA/strings.xml | 24 +++++++++++++++ .../values-mcc311-mnc480-en-rGB/strings.xml | 24 +++++++++++++++ .../values-mcc311-mnc480-en-rIN/strings.xml | 24 +++++++++++++++ .../values-mcc311-mnc480-en-rXC/strings.xml | 24 +++++++++++++++ .../values-mcc311-mnc480-es-rUS/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-es/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-et/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-eu/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-fa/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-fi/strings.xml | 24 +++++++++++++++ .../values-mcc311-mnc480-fr-rCA/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-fr/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-gl/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-gu/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-hi/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-hr/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-hu/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-hy/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-in/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-is/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-it/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-iw/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-ja/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-ka/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-kk/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-km/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-kn/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-ko/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-ky/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-lo/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-lt/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-lv/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-mk/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-ml/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-mn/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-mr/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-ms/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-my/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-nb/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-ne/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-nl/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-or/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-pa/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-pl/strings.xml | 24 +++++++++++++++ .../values-mcc311-mnc480-pt-rBR/strings.xml | 24 +++++++++++++++ .../values-mcc311-mnc480-pt-rPT/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-pt/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-ro/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-ru/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-si/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-sk/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-sl/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-sq/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-sr/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-sv/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-sw/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-ta/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-te/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-th/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-tl/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-tr/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-uk/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-ur/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-uz/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-vi/strings.xml | 24 +++++++++++++++ .../values-mcc311-mnc480-zh-rCN/strings.xml | 24 +++++++++++++++ .../values-mcc311-mnc480-zh-rHK/strings.xml | 24 +++++++++++++++ .../values-mcc311-mnc480-zh-rTW/strings.xml | 24 +++++++++++++++ .../res/values-mcc311-mnc480-zu/strings.xml | 24 +++++++++++++++ Tethering/res/values-mk/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-ml/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-mn/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-mr/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-ms/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-my/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-nb/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-ne/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-nl/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-or/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-pa/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-pl/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-pt-rBR/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-pt-rPT/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-pt/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-ro/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-ru/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-si/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-sk/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-sl/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-sq/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-sr/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-sv/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-sw/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-ta/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-te/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-th/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-tl/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-tr/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-uk/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-ur/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-uz/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-vi/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-zh-rCN/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-zh-rHK/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-zh-rTW/strings.xml | 29 ++++++++++++++++--- Tethering/res/values-zu/strings.xml | 29 ++++++++++++++++--- 255 files changed, 6205 insertions(+), 340 deletions(-) create mode 100644 Tethering/res/values-mcc310-mnc004-af/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-am/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-ar/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-as/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-az/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-b+sr+Latn/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-be/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-bg/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-bn/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-bs/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-ca/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-cs/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-da/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-de/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-el/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-en-rAU/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-en-rCA/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-en-rGB/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-en-rIN/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-en-rXC/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-es-rUS/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-es/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-et/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-eu/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-fa/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-fi/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-fr-rCA/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-fr/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-gl/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-gu/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-hi/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-hr/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-hu/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-hy/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-in/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-is/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-it/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-iw/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-ja/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-ka/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-kk/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-km/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-kn/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-ko/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-ky/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-lo/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-lt/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-lv/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-mk/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-ml/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-mn/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-mr/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-ms/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-my/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-nb/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-ne/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-nl/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-or/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-pa/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-pl/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-pt-rBR/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-pt-rPT/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-pt/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-ro/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-ru/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-si/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-sk/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-sl/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-sq/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-sr/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-sv/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-sw/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-ta/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-te/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-th/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-tl/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-tr/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-uk/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-ur/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-uz/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-vi/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-zh-rCN/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-zh-rHK/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-zh-rTW/strings.xml create mode 100644 Tethering/res/values-mcc310-mnc004-zu/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-af/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-am/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-ar/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-as/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-az/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-b+sr+Latn/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-be/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-bg/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-bn/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-bs/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-ca/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-cs/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-da/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-de/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-el/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-en-rAU/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-en-rCA/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-en-rGB/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-en-rIN/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-en-rXC/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-es-rUS/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-es/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-et/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-eu/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-fa/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-fi/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-fr-rCA/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-fr/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-gl/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-gu/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-hi/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-hr/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-hu/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-hy/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-in/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-is/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-it/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-iw/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-ja/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-ka/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-kk/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-km/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-kn/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-ko/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-ky/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-lo/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-lt/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-lv/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-mk/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-ml/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-mn/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-mr/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-ms/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-my/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-nb/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-ne/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-nl/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-or/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-pa/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-pl/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-pt-rBR/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-pt-rPT/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-pt/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-ro/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-ru/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-si/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-sk/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-sl/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-sq/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-sr/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-sv/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-sw/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-ta/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-te/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-th/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-tl/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-tr/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-uk/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-ur/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-uz/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-vi/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-zh-rCN/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-zh-rHK/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-zh-rTW/strings.xml create mode 100644 Tethering/res/values-mcc311-mnc480-zu/strings.xml diff --git a/Tethering/res/values-af/strings.xml b/Tethering/res/values-af/strings.xml index 1258805378..056168b12e 100644 --- a/Tethering/res/values-af/strings.xml +++ b/Tethering/res/values-af/strings.xml @@ -1,8 +1,29 @@ + + - "Verbinding of Wi-Fi-warmkol aktief" - "Tik om op te stel." - "Verbinding is gedeaktiveer" - "Kontak jou administrateur vir besonderhede" + "Verbinding of warmkol is aktief" + "Tik om op te stel." + "Verbinding is gedeaktiveer" + "Kontak jou administrateur vir besonderhede" + "Warmkol- en verbindingstatus" + + + + + diff --git a/Tethering/res/values-am/strings.xml b/Tethering/res/values-am/strings.xml index 9c36192257..ac468dd144 100644 --- a/Tethering/res/values-am/strings.xml +++ b/Tethering/res/values-am/strings.xml @@ -1,8 +1,29 @@ + + - "መሰካት ወይም ገባሪ ድረስ ነጥብ" - "ለማዋቀር መታ ያድርጉ።" - "እንደ ሞደም መሰካት ተሰናክሏል" - "ለዝርዝሮች የእርስዎን አስተዳዳሪ ያነጋግሩ" + "እንደ ሞደም መሰካት ወይም መገናኛ ነጥብ ገባሪ" + "ለማዋቀር መታ ያድርጉ።" + "እንደ ሞደም መሰካት ተሰናክሏል" + "ለዝርዝሮች የእርስዎን አስተዳዳሪ ያነጋግሩ" + "መገናኛ ነጥብ እና እንደ ሞደም የመሰካት ሁኔታ" + + + + + diff --git a/Tethering/res/values-ar/strings.xml b/Tethering/res/values-ar/strings.xml index 9f84ce4090..7d5bad34da 100644 --- a/Tethering/res/values-ar/strings.xml +++ b/Tethering/res/values-ar/strings.xml @@ -1,8 +1,29 @@ + + - "النطاق أو نقطة الاتصال نشطة" - "انقر للإعداد." - "تم إيقاف التوصيل" - "اتصل بالمشرف للحصول على التفاصيل" + "النطاق نشط أو نقطة الاتصال نشطة" + "انقر للإعداد." + "التوصيل متوقف." + "تواصَل مع المشرف للحصول على التفاصيل." + "حالة نقطة الاتصال والتوصيل" + + + + + diff --git a/Tethering/res/values-as/strings.xml b/Tethering/res/values-as/strings.xml index 8855822e7c..091350455b 100644 --- a/Tethering/res/values-as/strings.xml +++ b/Tethering/res/values-as/strings.xml @@ -1,8 +1,29 @@ + + - "টেডাৰিং বা হটস্প\'ট সক্ৰিয় অৱস্থাত আছে" - "ছেট আপ কৰিবলৈ টিপক।" - "টেডাৰিং অক্ষম কৰি থোৱা হৈছে" - "সবিশেষ জানিবলৈ আপোনাৰ প্ৰশাসকৰ সৈতে যোগাযোগ কৰক" + "টে\'ডাৰিং অথবা হ\'টস্প\'ট সক্ৰিয় অৱস্থাত আছে" + "ছেট আপ কৰিবলৈ টিপক।" + "টে\'ডাৰিঙৰ সুবিধাটো অক্ষম কৰি থোৱা হৈছে" + "সবিশেষ জানিবলৈ আপোনাৰ প্ৰশাসকৰ সৈতে যোগাযোগ কৰক" + "হ’টস্প\'ট আৰু টে\'ডাৰিঙৰ স্থিতি" + + + + + diff --git a/Tethering/res/values-az/strings.xml b/Tethering/res/values-az/strings.xml index eba50eb636..dce70da178 100644 --- a/Tethering/res/values-az/strings.xml +++ b/Tethering/res/values-az/strings.xml @@ -1,8 +1,29 @@ + + - "Tezerinq və ya hotspot aktivdir" - "Quraşdırmaq üçün tıklayın." - "Birləşmə deaktivdir" - "Məlumat üçün adminlə əlaqə saxlayın" + "Birləşmə və ya hotspot aktivdir" + "Ayarlamaq üçün toxunun." + "Birləşmə deaktivdir" + "Detallar üçün adminlə əlaqə saxlayın" + "Hotspot & birləşmə statusu" + + + + + diff --git a/Tethering/res/values-b+sr+Latn/strings.xml b/Tethering/res/values-b+sr+Latn/strings.xml index 5b0e488ba5..b0774ec9a8 100644 --- a/Tethering/res/values-b+sr+Latn/strings.xml +++ b/Tethering/res/values-b+sr+Latn/strings.xml @@ -1,8 +1,29 @@ + + - "Aktivno povezivanje sa internetom preko mobilnog uređaja ili hotspot" - "Dodirnite da biste podesili." - "Privezivanje je onemogućeno" - "Potražite detalje od administratora" + "Privezivanje ili hotspot je aktivan" + "Dodirnite da biste podesili." + "Privezivanje je onemogućeno" + "Potražite detalje od administratora" + "Status hotspota i privezivanja" + + + + + diff --git a/Tethering/res/values-be/strings.xml b/Tethering/res/values-be/strings.xml index 5966c7155e..a8acebe2e9 100644 --- a/Tethering/res/values-be/strings.xml +++ b/Tethering/res/values-be/strings.xml @@ -1,8 +1,29 @@ + + - "USB-мадэм або хот-спот Wi-Fi актыўныя" - "Дакраніцеся, каб наладзіць." - "Рэжым мадэма адключаны" - "Звярніцеся да адміністратара па падрабязную інфармацыю" + "Мадэм або хот-спот актыўныя" + "Дакраніцеся, каб наладзіць." + "Рэжым мадэма выключаны" + "Звярніцеся да адміністратара па падрабязную інфармацыю" + "Стан \"Хот-спот і мадэм\"" + + + + + diff --git a/Tethering/res/values-bg/strings.xml b/Tethering/res/values-bg/strings.xml index ed58d7311a..94fb2d8f17 100644 --- a/Tethering/res/values-bg/strings.xml +++ b/Tethering/res/values-bg/strings.xml @@ -1,8 +1,29 @@ + + - "Има активна споделена връзка или безжична точка за достъп" - "Докоснете, за да настроите." - "Функцията за тетъринг е деактивирана" - "Свържете се с администратора си за подробности" + "Има активна споделена връзка или точка за достъп" + "Докоснете, за да настроите." + "Функцията за тетъринг е деактивирана" + "Свържете се с администратора си за подробности" + "Състояние на функцията за точка за достъп и тетъринг" + + + + + diff --git a/Tethering/res/values-bn/strings.xml b/Tethering/res/values-bn/strings.xml index 8d9880aa9a..aea02b9ddf 100644 --- a/Tethering/res/values-bn/strings.xml +++ b/Tethering/res/values-bn/strings.xml @@ -1,8 +1,29 @@ + + - "টিথারিং বা হটস্পট সক্রিয় আছে" - "সেট-আপ করার জন্য আলতো চাপুন৷" - "টিথারিং অক্ষম করা আছে" - "বিশদ বিবরণের জন্য প্রশাসকের সাথে যোগাযোগ করুন" + "টিথারিং বা হটস্পট চালু আছে" + "সেট-আপ করতে ট্যাপ করুন।" + "টিথারিং বন্ধ করা আছে" + "বিশদে জানতে অ্যাডমিনের সাথে যোগাযোগ করুন" + "হটস্পট ও টিথারিং স্ট্যাটাস" + + + + + diff --git a/Tethering/res/values-bs/strings.xml b/Tethering/res/values-bs/strings.xml index 2361b9dd38..de232724c5 100644 --- a/Tethering/res/values-bs/strings.xml +++ b/Tethering/res/values-bs/strings.xml @@ -1,8 +1,29 @@ + + - "Uređaj dijeli vezu ili djeluje kao pristupna tačka" - "Dodirnite za postavke" - "Povezivanje putem mobitela je onemogućeno" - "Kontaktirajte svog administratora za dodatne detalje" + "Aktivno je povezivanje putem mobitela ili pristupna tačka" + "Dodirnite da postavite." + "Povezivanje putem mobitela je onemogućeno" + "Kontaktirajte svog administratora za detalje" + "Status pristupne tačke i povezivanja putem mobitela" + + + + + diff --git a/Tethering/res/values-ca/strings.xml b/Tethering/res/values-ca/strings.xml index 6752b519e2..88b795c1f8 100644 --- a/Tethering/res/values-ca/strings.xml +++ b/Tethering/res/values-ca/strings.xml @@ -1,8 +1,29 @@ + + - "Compartició de xarxa o punt d\'accés Wi-Fi activat" - "Toca per configurar." - "La compartició de xarxa està desactivada" - "Contacta amb el teu administrador per obtenir més informació" + "Compartició de xarxa o punt d\'accés Wi‑Fi actius" + "Toca per configurar." + "La compartició de xarxa està desactivada" + "Contacta amb el teu administrador per obtenir més informació" + "Estat del punt d\'accés Wi‑Fi i de la compartició de xarxa" + + + + + diff --git a/Tethering/res/values-cs/strings.xml b/Tethering/res/values-cs/strings.xml index 5fdd53adf1..8c1b83bf3e 100644 --- a/Tethering/res/values-cs/strings.xml +++ b/Tethering/res/values-cs/strings.xml @@ -1,8 +1,29 @@ + + - "Sdílené připojení nebo hotspot je aktivní." - "Klepnutím zahájíte nastavení." - "Tethering je zakázán" - "O podrobnosti požádejte administrátora" + "Tethering nebo hotspot je aktivní" + "Klepnutím zahájíte nastavení." + "Tethering je zakázán" + "O podrobnosti požádejte administrátora" + "Stav hotspotu a tetheringu" + + + + + diff --git a/Tethering/res/values-da/strings.xml b/Tethering/res/values-da/strings.xml index 2775dfa551..f413e70548 100644 --- a/Tethering/res/values-da/strings.xml +++ b/Tethering/res/values-da/strings.xml @@ -1,8 +1,29 @@ + + - "Netdeling eller hotspot er aktivt" - "Tryk for at konfigurere" - "Netdeling er deaktiveret" - "Kontakt din administrator for at få oplysninger" + "Netdeling eller hotspot er aktivt" + "Tryk for at konfigurere." + "Netdeling er deaktiveret" + "Kontakt din administrator for at få oplysninger" + "Status for hotspot og netdeling" + + + + + diff --git a/Tethering/res/values-de/strings.xml b/Tethering/res/values-de/strings.xml index 9046cd5e11..f057d7824e 100644 --- a/Tethering/res/values-de/strings.xml +++ b/Tethering/res/values-de/strings.xml @@ -1,8 +1,29 @@ + + - "Tethering oder Hotspot aktiv" - "Zum Einrichten tippen." - "Tethering ist deaktiviert" - "Bitte wende dich für weitere Informationen an den Administrator" + "Tethering oder Hotspot aktiv" + "Zum Einrichten tippen." + "Tethering ist deaktiviert" + "Bitte wende dich für weitere Informationen an den Administrator" + "Hotspot- und Tethering-Status" + + + + + diff --git a/Tethering/res/values-el/strings.xml b/Tethering/res/values-el/strings.xml index 3b9f53733b..b3c986bdaf 100644 --- a/Tethering/res/values-el/strings.xml +++ b/Tethering/res/values-el/strings.xml @@ -1,8 +1,29 @@ + + - "Πρόσδεση ή σύνδεση σημείου πρόσβασης ενεργή" - "Πατήστε για ρύθμιση." - "Η σύνδεση είναι απενεργοποιημένη" - "Επικοινωνήστε με τον διαχειριστή σας για λεπτομέρειες" + "Πρόσδεση ή σύνδεση σημείου πρόσβασης ενεργή" + "Πατήστε για ρύθμιση." + "Η σύνδεση είναι απενεργοποιημένη" + "Επικοινωνήστε με τον διαχειριστή σας για λεπτομέρειες" + "Κατάσταση σημείου πρόσβασης Wi-Fi και σύνδεσης" + + + + + diff --git a/Tethering/res/values-en-rAU/strings.xml b/Tethering/res/values-en-rAU/strings.xml index 56b88a5fb3..769e01208a 100644 --- a/Tethering/res/values-en-rAU/strings.xml +++ b/Tethering/res/values-en-rAU/strings.xml @@ -1,8 +1,29 @@ + + - "Tethering or hotspot active" - "Tap to set up." - "Tethering is disabled" - "Contact your admin for details" + "Tethering or hotspot active" + "Tap to set up." + "Tethering is disabled" + "Contact your admin for details" + "Hotspot and tethering status" + + + + + diff --git a/Tethering/res/values-en-rCA/strings.xml b/Tethering/res/values-en-rCA/strings.xml index 56b88a5fb3..769e01208a 100644 --- a/Tethering/res/values-en-rCA/strings.xml +++ b/Tethering/res/values-en-rCA/strings.xml @@ -1,8 +1,29 @@ + + - "Tethering or hotspot active" - "Tap to set up." - "Tethering is disabled" - "Contact your admin for details" + "Tethering or hotspot active" + "Tap to set up." + "Tethering is disabled" + "Contact your admin for details" + "Hotspot and tethering status" + + + + + diff --git a/Tethering/res/values-en-rGB/strings.xml b/Tethering/res/values-en-rGB/strings.xml index 56b88a5fb3..769e01208a 100644 --- a/Tethering/res/values-en-rGB/strings.xml +++ b/Tethering/res/values-en-rGB/strings.xml @@ -1,8 +1,29 @@ + + - "Tethering or hotspot active" - "Tap to set up." - "Tethering is disabled" - "Contact your admin for details" + "Tethering or hotspot active" + "Tap to set up." + "Tethering is disabled" + "Contact your admin for details" + "Hotspot and tethering status" + + + + + diff --git a/Tethering/res/values-en-rIN/strings.xml b/Tethering/res/values-en-rIN/strings.xml index 56b88a5fb3..769e01208a 100644 --- a/Tethering/res/values-en-rIN/strings.xml +++ b/Tethering/res/values-en-rIN/strings.xml @@ -1,8 +1,29 @@ + + - "Tethering or hotspot active" - "Tap to set up." - "Tethering is disabled" - "Contact your admin for details" + "Tethering or hotspot active" + "Tap to set up." + "Tethering is disabled" + "Contact your admin for details" + "Hotspot and tethering status" + + + + + diff --git a/Tethering/res/values-en-rXC/strings.xml b/Tethering/res/values-en-rXC/strings.xml index 7f47fc89d2..f1674bed4e 100644 --- a/Tethering/res/values-en-rXC/strings.xml +++ b/Tethering/res/values-en-rXC/strings.xml @@ -1,8 +1,29 @@ + + - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‏‎‏‎‏‎‏‏‎‏‎‎‏‏‎‏‏‎‏‏‏‎‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‎‏‏‎‎‎‏‏‎‏‎‏‏‏‎‏‎‎‏‎Tethering or hotspot active‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‎‏‎‏‎‏‎‏‎‎‎‏‏‏‎‏‏‏‏‏‎‎‎‎‏‏‏‏‎‎‎‎‏‏‏‏‎‎‎‏‏‏‎‏‏‏‏‎‎‏‎‏‎‎‎‏‎Tap to set up.‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‏‏‎‏‎‏‎‎‏‏‎‎‏‎‏‎‎‎‏‏‏‏‎‏‏‏‏‏‏‎‏‏‏‏‎‏‎‎‎‏‏‏‎‎‎‏‎‎‏‎‏‎‏‏‎Tethering is disabled‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‎‏‏‎‏‏‏‎‎‏‎‏‏‎‏‏‎‏‎‏‎‏‎‎‏‏‎‎‎‎‎‎‎‏‎‎‏‎‏‏‏‎‏‎‎‎‏‏‎‎‎‎‏‏‏‏‎Contact your admin for details‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‎‏‎‏‏‏‏‏‎‏‏‏‏‎‏‏‎‎‎‏‎‎‎‎‎‏‏‎‏‏‏‏‎‎‎‎‏‏‏‎‎‏‎‎‏‎‏‏‎‏‏‎‎‎‎‎Tethering or hotspot active‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‎‎‏‏‏‎‎‏‏‎‎‎‏‏‏‎‎‎‎‎‎‎‏‏‏‎‎‏‏‏‏‎‏‏‏‏‏‎‏‏‏‏‎‎‏‎‏‏‎‏‎‎‎‏‏‎‎‎‎Tap to set up.‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‏‏‎‏‏‎‎‏‎‎‎‏‎‏‎‎‏‎‎‏‎‎‎‏‎‎‎‎‏‏‏‎‏‎‎‎‏‏‏‎‎‎‏‎‏‎‎‎‏‏‎‎‏‏‏‏‏‎Tethering is disabled‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‏‏‏‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‏‎‏‏‏‏‏‎‎‏‏‎‎‏‎‎‏‎‏‎‎‏‏‏‎‏‎‏‎‏‎‎‏‎‎‎Contact your admin for details‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‏‏‎‏‏‎‏‎‎‎‎‏‏‎‎‏‎‎‏‏‎‎‎‎‏‏‎‏‏‎‏‏‎‎‎‏‏‏‏‏‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎Hotspot & tethering status‎‏‎‎‏‎" + + + + + diff --git a/Tethering/res/values-es-rUS/strings.xml b/Tethering/res/values-es-rUS/strings.xml index e4618b8cec..63689f4399 100644 --- a/Tethering/res/values-es-rUS/strings.xml +++ b/Tethering/res/values-es-rUS/strings.xml @@ -1,8 +1,29 @@ + + - "Anclaje a red o zona activa conectados" - "Presiona para configurar." - "Se inhabilitó la conexión mediante dispositivo portátil" - "Para obtener más información, comunícate con el administrador" + "Conexión a red o hotspot conectados" + "Presiona para configurar esta opción." + "Se inhabilitó la conexión mediante dispositivo portátil" + "Para obtener más información, comunícate con el administrador" + "Estado del hotspot y la conexión mediante dispositivo portátil" + + + + + diff --git a/Tethering/res/values-es/strings.xml b/Tethering/res/values-es/strings.xml index 8dc1575ce8..9a34ed5e38 100644 --- a/Tethering/res/values-es/strings.xml +++ b/Tethering/res/values-es/strings.xml @@ -1,8 +1,29 @@ + + - "Compartir conexión/Zona Wi-Fi activada" - "Toca para configurar." - "La conexión compartida está inhabilitada" - "Ponte en contacto con el administrador para obtener más información" + "Conexión compartida o punto de acceso activos" + "Toca para configurar." + "La conexión compartida está inhabilitada" + "Solicita más información a tu administrador" + "Estado del punto de acceso y de la conexión compartida" + + + + + diff --git a/Tethering/res/values-et/strings.xml b/Tethering/res/values-et/strings.xml index 872c8a74cc..0970341ab0 100644 --- a/Tethering/res/values-et/strings.xml +++ b/Tethering/res/values-et/strings.xml @@ -1,8 +1,29 @@ + + - "Jagamine või kuumkoht on aktiivne" - "Puudutage seadistamiseks." - "Jagamine on keelatud" - "Lisateabe saamiseks võtke ühendust oma administraatoriga" + "Jagamine või kuumkoht on aktiivne" + "Puudutage seadistamiseks." + "Jagamine on keelatud" + "Lisateabe saamiseks võtke ühendust oma administraatoriga" + "Kuumkoha ja jagamise olek" + + + + + diff --git a/Tethering/res/values-eu/strings.xml b/Tethering/res/values-eu/strings.xml index 6c4605e616..632019e2ef 100644 --- a/Tethering/res/values-eu/strings.xml +++ b/Tethering/res/values-eu/strings.xml @@ -1,8 +1,29 @@ + + - "Konexioa partekatzea edo sare publikoa aktibo" - "Sakatu konfiguratzeko." - "Desgaituta dago konexioa partekatzeko aukera" - "Xehetasunak lortzeko, jarri administratzailearekin harremanetan" + "Konexioa partekatzea edo wifi-gunea aktibo dago" + "Sakatu konfiguratzeko." + "Desgaituta dago konexioa partekatzeko aukera" + "Xehetasunak lortzeko, jarri administratzailearekin harremanetan" + "Wifi-gunearen eta konexioa partekatzeko eginbidearen egoera" + + + + + diff --git a/Tethering/res/values-fa/strings.xml b/Tethering/res/values-fa/strings.xml index bc2ee23609..2e21c85fa1 100644 --- a/Tethering/res/values-fa/strings.xml +++ b/Tethering/res/values-fa/strings.xml @@ -1,8 +1,29 @@ + + - "اشتراک‌گذاری اینترنت یا نقطه اتصال فعال" - "برای راه‌اندازی ضربه بزنید." - "اشتراک‌گذاری اینترنت غیرفعال است" - "برای جزئیات، با سرپرستتان تماس بگیرید" + "اشتراک‌گذاری اینترنت یا نقطه اتصال فعال" + "برای راه‌اندازی ضربه بزنید." + "اشتراک‌گذاری اینترنت غیرفعال است" + "برای جزئیات، با سرپرستتان تماس بگیرید" + "وضعیت نقطه اتصال و اشتراک‌گذاری اینترنت" + + + + + diff --git a/Tethering/res/values-fi/strings.xml b/Tethering/res/values-fi/strings.xml index ff0fca6502..413db3f0f8 100644 --- a/Tethering/res/values-fi/strings.xml +++ b/Tethering/res/values-fi/strings.xml @@ -1,8 +1,29 @@ + + - "Internetin jakaminen tai yhteyspiste käytössä" - "Määritä napauttamalla." - "Yhteyden jakaminen poistettu käytöstä" - "Kysy lisätietoja järjestelmänvalvojalta." + "Yhteyden jakaminen tai hotspot käytössä" + "Ota käyttöön napauttamalla." + "Yhteyden jakaminen on poistettu käytöstä" + "Pyydä lisätietoja järjestelmänvalvojalta" + "Hotspotin ja yhteyden jakamisen tila" + + + + + diff --git a/Tethering/res/values-fr-rCA/strings.xml b/Tethering/res/values-fr-rCA/strings.xml index 1f5df0ee0c..eb2e4ba540 100644 --- a/Tethering/res/values-fr-rCA/strings.xml +++ b/Tethering/res/values-fr-rCA/strings.xml @@ -1,8 +1,29 @@ + + - "Partage de connexion ou point d\'accès sans fil activé" - "Touchez pour configurer." - "Le partage de connexion est désactivé" - "Communiquez avec votre administrateur pour obtenir plus de détails" + "Partage de connexion ou point d\'accès sans fil activé" + "Touchez pour configurer." + "Le partage de connexion est désactivé" + "Communiquez avec votre administrateur pour obtenir plus de détails" + "Point d\'accès et partage de connexion" + + + + + diff --git a/Tethering/res/values-fr/strings.xml b/Tethering/res/values-fr/strings.xml index daf7c9d830..22259c52ab 100644 --- a/Tethering/res/values-fr/strings.xml +++ b/Tethering/res/values-fr/strings.xml @@ -1,8 +1,29 @@ + + - "Partage de connexion ou point d\'accès sans fil activé" - "Appuyez ici pour configurer." - "Le partage de connexion est désactivé" - "Pour en savoir plus, contactez votre administrateur" + "Partage de connexion ou point d\'accès activé" + "Appuyez pour effectuer la configuration." + "Le partage de connexion est désactivé" + "Pour en savoir plus, contactez votre administrateur" + "État du point d\'accès et du partage de connexion" + + + + + diff --git a/Tethering/res/values-gl/strings.xml b/Tethering/res/values-gl/strings.xml index 0d16a1de09..ded82fcd54 100644 --- a/Tethering/res/values-gl/strings.xml +++ b/Tethering/res/values-gl/strings.xml @@ -1,8 +1,29 @@ + + - "Conexión compartida ou zona wifi activada" - "Tocar para configurar." - "A conexión compartida está desactivada" - "Contacta co administrador para obter información" + "Conexión compartida ou zona wifi activada" + "Toca para configurar." + "A conexión compartida está desactivada" + "Contacta co administrador para obter información" + "Estado da zona wifi e da conexión compartida" + + + + + diff --git a/Tethering/res/values-gu/strings.xml b/Tethering/res/values-gu/strings.xml index 9d6b02f85f..7cbbc2de3d 100644 --- a/Tethering/res/values-gu/strings.xml +++ b/Tethering/res/values-gu/strings.xml @@ -1,8 +1,29 @@ + + - "ટિથરિંગ અથવા હૉટસ્પૉટ સક્રિય" - "સેટ કરવા માટે ટૅપ કરો." - "ટિથરિંગ અક્ષમ કરેલ છે" - "વિગતો માટે તમારા વ્યવસ્થાપકનો સંપર્ક કરો" + "ઇન્ટરનેટ શેર કરવાની સુવિધા અથવા હૉટસ્પૉટ સક્રિય છે" + "સેટઅપ કરવા માટે ટૅપ કરો." + "ઇન્ટરનેટ શેર કરવાની સુવિધા બંધ કરી છે" + "વિગતો માટે તમારા વ્યવસ્થાપકનો સંપર્ક કરો" + "હૉટસ્પૉટ અને ઇન્ટરનેટ શેર કરવાની સુવિધાનું સ્ટેટસ" + + + + + diff --git a/Tethering/res/values-hi/strings.xml b/Tethering/res/values-hi/strings.xml index 9c29d9a8f9..08af81b826 100644 --- a/Tethering/res/values-hi/strings.xml +++ b/Tethering/res/values-hi/strings.xml @@ -1,8 +1,29 @@ + + - "टेदरिंग या हॉटस्‍पॉट सक्रिय" - "सेट करने के लिए टैप करें." - "टेदरिंग अक्षम है" - "जानकारी के लिए अपने एडमिन से संपर्क करें" + "टेदरिंग या हॉटस्पॉट चालू है" + "सेट अप करने के लिए टैप करें." + "टेदरिंग बंद है" + "जानकारी के लिए अपने एडमिन से संपर्क करें" + "हॉटस्पॉट और टेदरिंग की स्थिति" + + + + + diff --git a/Tethering/res/values-hr/strings.xml b/Tethering/res/values-hr/strings.xml index d0d25bb755..827c135f20 100644 --- a/Tethering/res/values-hr/strings.xml +++ b/Tethering/res/values-hr/strings.xml @@ -1,8 +1,29 @@ + + - "Ograničenje ili aktivan hotspot" - "Dodirnite da biste postavili." - "Modemsko je povezivanje onemogućeno" - "Obratite se administratoru da biste saznali pojedinosti" + "Modemsko povezivanje ili žarišna točka aktivni" + "Dodirnite da biste postavili." + "Modemsko je povezivanje onemogućeno" + "Obratite se administratoru da biste saznali pojedinosti" + "Status žarišne točke i modemskog povezivanja" + + + + + diff --git a/Tethering/res/values-hu/strings.xml b/Tethering/res/values-hu/strings.xml index 3129659923..eb68d6babf 100644 --- a/Tethering/res/values-hu/strings.xml +++ b/Tethering/res/values-hu/strings.xml @@ -1,8 +1,29 @@ + + - "Megosztás vagy aktív hotspot" - "Koppintson a beállításhoz." - "Az internetmegosztás le van tiltva" - "A részletekért forduljon rendszergazdájához" + "Megosztás vagy aktív hotspot" + "Koppintson a beállításhoz." + "Az internetmegosztás le van tiltva" + "A részletekért forduljon rendszergazdájához" + "Hotspot és internetmegosztás állapota" + + + + + diff --git a/Tethering/res/values-hy/strings.xml b/Tethering/res/values-hy/strings.xml index 8ba6435fd5..912941e538 100644 --- a/Tethering/res/values-hy/strings.xml +++ b/Tethering/res/values-hy/strings.xml @@ -1,8 +1,29 @@ + + - "Մոդեմի ռեժիմը միացված է" - "Հպեք՝ կարգավորելու համար:" - "Մոդեմի ռեժիմն անջատված է" - "Մանրամասների համար դիմեք ձեր ադմինիստրատորին" + "Մոդեմի ռեժիմը միացված է" + "Հպեք՝ կարգավորելու համար։" + "Մոդեմի ռեժիմն անջատված է" + "Մանրամասների համար դիմեք ձեր ադմինիստրատորին" + "Թեժ կետի և մոդեմի ռեժիմի կարգավիճակը" + + + + + diff --git a/Tethering/res/values-in/strings.xml b/Tethering/res/values-in/strings.xml index 1e093ab237..a4e175a439 100644 --- a/Tethering/res/values-in/strings.xml +++ b/Tethering/res/values-in/strings.xml @@ -1,8 +1,29 @@ + + - "Tethering (Penambatan) atau hotspot aktif" - "Ketuk untuk menyiapkan." - "Tethering dinonaktifkan" - "Hubungi admin untuk mengetahui detailnya" + "Tethering atau hotspot aktif" + "Ketuk untuk menyiapkan." + "Tethering dinonaktifkan" + "Hubungi admin untuk mengetahui detailnya" + "Status hotspot & tethering" + + + + + diff --git a/Tethering/res/values-is/strings.xml b/Tethering/res/values-is/strings.xml index f5769d5344..e9f6670bcd 100644 --- a/Tethering/res/values-is/strings.xml +++ b/Tethering/res/values-is/strings.xml @@ -1,8 +1,29 @@ + + - "Kveikt á tjóðrun eða aðgangsstað" - "Ýttu til að setja upp." - "Slökkt er á tjóðrun" - "Hafðu samband við kerfisstjórann til að fá upplýsingar" + "Kveikt á tjóðrun eða aðgangsstað" + "Ýttu til að setja upp." + "Slökkt er á tjóðrun" + "Hafðu samband við kerfisstjórann til að fá upplýsingar" + "Staða heits reits og tjóðrunar" + + + + + diff --git a/Tethering/res/values-it/strings.xml b/Tethering/res/values-it/strings.xml index e0b3724325..ffb9196f5e 100644 --- a/Tethering/res/values-it/strings.xml +++ b/Tethering/res/values-it/strings.xml @@ -1,8 +1,29 @@ + + - "Tethering oppure hotspot attivo" - "Tocca per impostare." - "Tethering disattivato" - "Contatta il tuo amministratore per avere informazioni dettagliate" + "Hotspot o tethering attivo" + "Tocca per impostare." + "Tethering disattivato" + "Contatta il tuo amministratore per avere informazioni dettagliate" + "Stato hotspot e tethering" + + + + + diff --git a/Tethering/res/values-iw/strings.xml b/Tethering/res/values-iw/strings.xml index c002c44b23..7adcb47350 100644 --- a/Tethering/res/values-iw/strings.xml +++ b/Tethering/res/values-iw/strings.xml @@ -1,8 +1,29 @@ + + - "שיתוף אינטרנט פעיל" - "הקש כדי להגדיר." - "שיתוף האינטרנט בין ניידים מושבת" - "לפרטים, יש לפנות למנהל המערכת" + "נקודה לשיתוף אינטרנט או שיתוף אינטרנט בין מכשירים: בסטטוס פעיל" + "יש להקיש כדי להגדיר." + "שיתוף האינטרנט בין מכשירים מושבת" + "לפרטים, יש לפנות למנהל המערכת" + "סטטוס של נקודה לשיתוף אינטרנט ושיתוף אינטרנט בין מכשירים" + + + + + diff --git a/Tethering/res/values-ja/strings.xml b/Tethering/res/values-ja/strings.xml index 314bde00df..f68a73010b 100644 --- a/Tethering/res/values-ja/strings.xml +++ b/Tethering/res/values-ja/strings.xml @@ -1,8 +1,29 @@ + + - "テザリングまたはアクセスポイントが有効です" - "タップしてセットアップします。" - "テザリングは無効に設定されています" - "詳しくは、管理者にお問い合わせください" + "テザリングまたはアクセス ポイントが有効です" + "タップしてセットアップします。" + "テザリングは無効に設定されています" + "詳しくは、管理者にお問い合わせください" + "アクセス ポイントとテザリングのステータス" + + + + + diff --git a/Tethering/res/values-ka/strings.xml b/Tethering/res/values-ka/strings.xml index 7bbd81d343..7c22e82bd3 100644 --- a/Tethering/res/values-ka/strings.xml +++ b/Tethering/res/values-ka/strings.xml @@ -1,8 +1,29 @@ + + - "ტეტერინგი ან უსადენო ქსელი აქტიურია" - "შეეხეთ დასაყენებლად." - "ტეტერინგი გათიშულია" - "დამატებითი ინფორმაციისთვის დაუკავშირდით თქვენს ადმინისტრატორს" + "ტეტერინგი ან უსადენო ქსელი აქტიურია" + "შეეხეთ დასაყენებლად." + "ტეტერინგი გათიშულია" + "დამატებითი ინფორმაციისთვის დაუკავშირდით თქვენს ადმინისტრატორს" + "უსადენო ქსელის და ტეტერინგის სტატუსი" + + + + + diff --git a/Tethering/res/values-kk/strings.xml b/Tethering/res/values-kk/strings.xml index 7fd87a1596..0857d06de2 100644 --- a/Tethering/res/values-kk/strings.xml +++ b/Tethering/res/values-kk/strings.xml @@ -1,8 +1,29 @@ + + - "Тетеринг немесе хотспот қосулы" - "Реттеу үшін түртіңіз." - "Тетеринг өшірілді" - "Мәліметтерді әкімшіден алыңыз" + "Тетеринг немесе хотспот қосулы" + "Реттеу үшін түртіңіз." + "Тетеринг өшірілді." + "Мәліметтерді әкімшіден алыңыз." + "Хотспот және тетеринг күйі" + + + + + diff --git a/Tethering/res/values-km/strings.xml b/Tethering/res/values-km/strings.xml index 2f85224679..536e3d1703 100644 --- a/Tethering/res/values-km/strings.xml +++ b/Tethering/res/values-km/strings.xml @@ -1,8 +1,29 @@ + + - "ភ្ជាប់ ឬ​ហតស្ពត​សកម្ម" - "ប៉ះដើម្បីកំណត់" - "ការភ្ជាប់​ត្រូវបានបិទ" - "ទាក់ទងអ្នកគ្រប់គ្រង​របស់អ្នកសម្រាប់​ព័ត៌មានលម្អិត" + "ការភ្ជាប់ ឬហតស្ប៉ត​កំពុងដំណើរការ" + "ចុច​ដើម្បី​រៀបចំ។" + "ការភ្ជាប់​ត្រូវបានបិទ" + "ទាក់ទងអ្នកគ្រប់គ្រង​របស់អ្នក ដើម្បីទទួលបានព័ត៌មានលម្អិត" + "ស្ថានភាពនៃការភ្ជាប់ និងហតស្ប៉ត" + + + + + diff --git a/Tethering/res/values-kn/strings.xml b/Tethering/res/values-kn/strings.xml index f11a83ea40..32f54926f4 100644 --- a/Tethering/res/values-kn/strings.xml +++ b/Tethering/res/values-kn/strings.xml @@ -1,8 +1,29 @@ + + - "ಟೆಥರಿಂಗ್ ಅಥವಾ ಹಾಟ್‌ಸ್ಪಾಟ್ ಸಕ್ರಿಯವಾಗಿದೆ" - "ಹೊಂದಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ." - "ಟೆಥರಿಂಗ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ" - "ವಿವರಗಳಿಗಾಗಿ ನಿಮ್ಮ ನಿರ್ವಾಹಕರನ್ನು ಸಂಪರ್ಕಿಸಿ" + "ಟೆಥರಿಂಗ್ ಅಥವಾ ಹಾಟ್‌ಸ್ಪಾಟ್ ಸಕ್ರಿಯವಾಗಿದೆ" + "ಸೆಟಪ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ." + "ಟೆಥರಿಂಗ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ" + "ವಿವರಗಳಿಗಾಗಿ ನಿಮ್ಮ ನಿರ್ವಾಹಕರನ್ನು ಸಂಪರ್ಕಿಸಿ" + "ಹಾಟ್‌ಸ್ಪಾಟ್ ಮತ್ತು ಟೆಥರಿಂಗ್‌ ಸ್ಥಿತಿ" + + + + + diff --git a/Tethering/res/values-ko/strings.xml b/Tethering/res/values-ko/strings.xml index 57f24f5b1a..156b24786d 100644 --- a/Tethering/res/values-ko/strings.xml +++ b/Tethering/res/values-ko/strings.xml @@ -1,8 +1,29 @@ + + - "테더링 또는 핫스팟 사용" - "설정하려면 탭하세요." - "테더링이 사용 중지됨" - "자세한 정보는 관리자에게 문의하세요." + "테더링 또는 핫스팟 사용" + "설정하려면 탭하세요." + "테더링이 사용 중지됨" + "자세한 정보는 관리자에게 문의하세요." + "핫스팟 및 테더링 상태" + + + + + diff --git a/Tethering/res/values-ky/strings.xml b/Tethering/res/values-ky/strings.xml index 79854859d4..18ee5fd357 100644 --- a/Tethering/res/values-ky/strings.xml +++ b/Tethering/res/values-ky/strings.xml @@ -1,8 +1,29 @@ + + - "Жалгаштыруу же хотспот жандырылган" - "Жөндөө үчүн таптап коюңуз." - "Жалгаштыруу функциясы өчүрүлгөн" - "Кеңири маалымат үчүн администраторуңузга кайрылыңыз" + "Модем режими күйүп турат" + "Жөндөө үчүн таптап коюңуз." + "Телефонду модем катары колдонууга болбойт" + "Кеңири маалымат үчүн администраторуңузга кайрылыңыз" + "Байланыш түйүнүнүн жана модем режиминин статусу" + + + + + diff --git a/Tethering/res/values-lo/strings.xml b/Tethering/res/values-lo/strings.xml index 78f1585f60..b12767018c 100644 --- a/Tethering/res/values-lo/strings.xml +++ b/Tethering/res/values-lo/strings.xml @@ -1,8 +1,29 @@ + + - "ເປີດ​ການ​ປ່ອຍ​ສັນຍານ ຫຼື​ຮັອດສະປອດ​ແລ້ວ" - "ແຕະເພື່ອຕັ້ງຄ່າ." - "ການປ່ອຍສັນຍານຖືກປິດໄວ້" - "ຕິດຕໍ່ຜູ້ເບິ່ງແຍງລະບົບສຳລັບລາຍລະອຽດ" + "ເປີດການປ່ອຍສັນຍານ ຫຼື ຮັອດສະປອດແລ້ວ" + "ແຕະເພື່ອຕັ້ງຄ່າ." + "ການປ່ອຍສັນຍານຖືກປິດໄວ້" + "ຕິດຕໍ່ຜູ້ເບິ່ງແຍງລະບົບສຳລັບລາຍລະອຽດ" + "ສະຖານະຮັອດສະປອດ ແລະ ການປ່ອຍສັນຍານ" + + + + + diff --git a/Tethering/res/values-lt/strings.xml b/Tethering/res/values-lt/strings.xml index ebff8ac9d1..8427baf39f 100644 --- a/Tethering/res/values-lt/strings.xml +++ b/Tethering/res/values-lt/strings.xml @@ -1,8 +1,29 @@ + + - "Susietas ar aktyvus" - "Palieskite, kad nustatytumėte." - "Įrenginio kaip modemo naudojimas išjungtas" - "Jei reikia išsamios informacijos, susisiekite su administratoriumi" + "Įrenginys naudojamas kaip modemas arba įjungtas viešosios interneto prieigos taškas" + "Palieskite, kad nustatytumėte." + "Įrenginio kaip modemo naudojimas išjungtas" + "Jei reikia išsamios informacijos, susisiekite su administratoriumi" + "Viešosios interneto prieigos taško ir įrenginio kaip modemo naudojimo būsena" + + + + + diff --git a/Tethering/res/values-lv/strings.xml b/Tethering/res/values-lv/strings.xml index 54d0048b52..aa2d6990e0 100644 --- a/Tethering/res/values-lv/strings.xml +++ b/Tethering/res/values-lv/strings.xml @@ -1,8 +1,29 @@ + + - "Piesaiste vai tīklājs ir aktīvs." - "Pieskarieties, lai iestatītu." - "Piesaiste ir atspējota" - "Lai iegūtu detalizētu informāciju, sazinieties ar savu administratoru." + "Piesaiste vai tīklājs ir aktīvs." + "Pieskarieties, lai to iestatītu." + "Piesaiste ir atspējota" + "Lai iegūtu detalizētu informāciju, sazinieties ar savu administratoru." + "Tīklāja un piesaistes statuss" + + + + + diff --git a/Tethering/res/values-mcc310-mnc004-af/strings.xml b/Tethering/res/values-mcc310-mnc004-af/strings.xml new file mode 100644 index 0000000000..19d659c6ce --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-af/strings.xml @@ -0,0 +1,24 @@ + + + + + "Verbinding het nie internet nie" + "Toestelle kan nie koppel nie" + "Skakel verbinding af" + "Warmkol of verbinding is aan" + "Bykomende heffings kan geld terwyl jy swerf" + diff --git a/Tethering/res/values-mcc310-mnc004-am/strings.xml b/Tethering/res/values-mcc310-mnc004-am/strings.xml new file mode 100644 index 0000000000..8995430b4f --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-am/strings.xml @@ -0,0 +1,24 @@ + + + + + "ማስተሳሰር ምንም በይነመረብ የለውም" + "መሣሪያዎችን ማገናኘት አይቻልም" + "ማስተሳሰርን አጥፋ" + "መገናኛ ነጥብ ወይም ማስተሳሰር በርቷል" + "በሚያንዣብብበት ጊዜ ተጨማሪ ክፍያዎች ተፈጻሚ ሊሆኑ ይችላሉ" + diff --git a/Tethering/res/values-mcc310-mnc004-ar/strings.xml b/Tethering/res/values-mcc310-mnc004-ar/strings.xml new file mode 100644 index 0000000000..54f3b5389a --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-ar/strings.xml @@ -0,0 +1,24 @@ + + + + + "ما مِن اتصال بالإنترنت خلال التوصيل" + "تعذّر اتصال الأجهزة" + "إيقاف التوصيل" + "نقطة الاتصال أو التوصيل مفعّلان" + "قد يتم تطبيق رسوم إضافية أثناء التجوال." + diff --git a/Tethering/res/values-mcc310-mnc004-as/strings.xml b/Tethering/res/values-mcc310-mnc004-as/strings.xml new file mode 100644 index 0000000000..e215141c9e --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-as/strings.xml @@ -0,0 +1,24 @@ + + + + + "টে\'ডাৰিঙৰ ইণ্টাৰনেট নাই" + "ডিভাইচসমূহ সংযোগ কৰিব নোৱাৰি" + "টে\'ডাৰিং অফ কৰক" + "হটস্পট অথবা টে\'ডাৰিং অন আছে" + "ৰ\'মিঙত থাকিলে অতিৰিক্ত মাচুল প্ৰযোজ্য হ’ব পাৰে" + diff --git a/Tethering/res/values-mcc310-mnc004-az/strings.xml b/Tethering/res/values-mcc310-mnc004-az/strings.xml new file mode 100644 index 0000000000..1fd8e4c963 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-az/strings.xml @@ -0,0 +1,24 @@ + + + + + "Modemin internetə girişi yoxdur" + "Cihazları qoşmaq mümkün deyil" + "Modemi deaktiv edin" + "Hotspot və ya modem aktivdir" + "Rouminq zamanı əlavə ödənişlər tətbiq edilə bilər" + diff --git a/Tethering/res/values-mcc310-mnc004-b+sr+Latn/strings.xml b/Tethering/res/values-mcc310-mnc004-b+sr+Latn/strings.xml new file mode 100644 index 0000000000..1abe4f3aa3 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-b+sr+Latn/strings.xml @@ -0,0 +1,24 @@ + + + + + "Privezivanje nema pristup internetu" + "Povezivanje uređaja nije uspelo" + "Isključi privezivanje" + "Uključen je hotspot ili privezivanje" + "Možda važe dodatni troškovi u romingu" + diff --git a/Tethering/res/values-mcc310-mnc004-be/strings.xml b/Tethering/res/values-mcc310-mnc004-be/strings.xml new file mode 100644 index 0000000000..38dbd1e391 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-be/strings.xml @@ -0,0 +1,24 @@ + + + + + "Рэжым мадэма выкарыстоўваецца без доступу да інтэрнэту" + "Не ўдалося падключыць прылады" + "Выключыць рэжым мадэма" + "Хот-спот або рэжым мадэма ўключаны" + "Пры выкарыстанні роўмінгу можа спаганяцца дадатковая плата" + diff --git a/Tethering/res/values-mcc310-mnc004-bg/strings.xml b/Tethering/res/values-mcc310-mnc004-bg/strings.xml new file mode 100644 index 0000000000..04b44db5c1 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-bg/strings.xml @@ -0,0 +1,24 @@ + + + + + "Тетърингът няма връзка с интернет" + "Устройствата не могат да установят връзка" + "Изключване на тетъринга" + "Точката за достъп или тетърингът са включени" + "Възможно е да ви бъдат начислени допълнителни такси при роуминг" + diff --git a/Tethering/res/values-mcc310-mnc004-bn/strings.xml b/Tethering/res/values-mcc310-mnc004-bn/strings.xml new file mode 100644 index 0000000000..579d1be1c1 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-bn/strings.xml @@ -0,0 +1,24 @@ + + + + + "টিথারিং করার জন্য কোনও ইন্টারনেট কানেকশন নেই" + "ডিভাইস কানেক্ট করতে পারছে না" + "টিথারিং বন্ধ করুন" + "হটস্পট বা টিথারিং চালু আছে" + "রোমিংয়ের সময় অতিরিক্ত চার্জ করা হতে পারে" + diff --git a/Tethering/res/values-mcc310-mnc004-bs/strings.xml b/Tethering/res/values-mcc310-mnc004-bs/strings.xml new file mode 100644 index 0000000000..9ce3efe6c3 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-bs/strings.xml @@ -0,0 +1,24 @@ + + + + + "Povezivanje putem mobitela nema internet" + "Uređaji se ne mogu povezati" + "Isključi povezivanje putem mobitela" + "Pristupna tačka ili povezivanje putem mobitela je uključeno" + "Mogu nastati dodatni troškovi u romingu" + diff --git a/Tethering/res/values-mcc310-mnc004-ca/strings.xml b/Tethering/res/values-mcc310-mnc004-ca/strings.xml new file mode 100644 index 0000000000..46d4c35b9b --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-ca/strings.xml @@ -0,0 +1,24 @@ + + + + + "La compartició de xarxa no té accés a Internet" + "No es poden connectar els dispositius" + "Desactiva la compartició de xarxa" + "S\'ha activat el punt d\'accés Wi‑Fi o la compartició de xarxa" + "És possible que s\'apliquin costos addicionals en itinerància" + diff --git a/Tethering/res/values-mcc310-mnc004-cs/strings.xml b/Tethering/res/values-mcc310-mnc004-cs/strings.xml new file mode 100644 index 0000000000..cc13860b3d --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-cs/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tethering nemá připojení k internetu" + "Zařízení se nemůžou připojit" + "Vypnout tethering" + "Je zapnutý hotspot nebo tethering" + "Při roamingu mohou být účtovány dodatečné poplatky" + diff --git a/Tethering/res/values-mcc310-mnc004-da/strings.xml b/Tethering/res/values-mcc310-mnc004-da/strings.xml new file mode 100644 index 0000000000..92c3ae1156 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-da/strings.xml @@ -0,0 +1,24 @@ + + + + + "Netdeling har ingen internetforbindelse" + "Enheder kan ikke oprette forbindelse" + "Deaktiver netdeling" + "Hotspot eller netdeling er aktiveret" + "Der opkræves muligvis yderligere gebyrer ved roaming" + diff --git a/Tethering/res/values-mcc310-mnc004-de/strings.xml b/Tethering/res/values-mcc310-mnc004-de/strings.xml new file mode 100644 index 0000000000..967eb4db2e --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-de/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tethering hat keinen Internetzugriff" + "Geräte können sich nicht verbinden" + "Tethering deaktivieren" + "Hotspot oder Tethering ist aktiviert" + "Für das Roaming können zusätzliche Gebühren anfallen" + diff --git a/Tethering/res/values-mcc310-mnc004-el/strings.xml b/Tethering/res/values-mcc310-mnc004-el/strings.xml new file mode 100644 index 0000000000..5fb497451f --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-el/strings.xml @@ -0,0 +1,24 @@ + + + + + "Η σύνδεση δεν έχει πρόσβαση στο διαδίκτυο" + "Δεν είναι δυνατή η σύνδεση των συσκευών" + "Απενεργοποιήστε τη σύνδεση" + "Ενεργό σημείο πρόσβασης Wi-Fi ή ενεργή σύνδεση" + "Ενδέχεται να ισχύουν επιπλέον χρεώσεις κατά την περιαγωγή." + diff --git a/Tethering/res/values-mcc310-mnc004-en-rAU/strings.xml b/Tethering/res/values-mcc310-mnc004-en-rAU/strings.xml new file mode 100644 index 0000000000..45647f93f2 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-en-rAU/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tethering has no Internet" + "Devices can’t connect" + "Turn off tethering" + "Hotspot or tethering is on" + "Additional charges may apply while roaming" + diff --git a/Tethering/res/values-mcc310-mnc004-en-rCA/strings.xml b/Tethering/res/values-mcc310-mnc004-en-rCA/strings.xml new file mode 100644 index 0000000000..45647f93f2 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-en-rCA/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tethering has no Internet" + "Devices can’t connect" + "Turn off tethering" + "Hotspot or tethering is on" + "Additional charges may apply while roaming" + diff --git a/Tethering/res/values-mcc310-mnc004-en-rGB/strings.xml b/Tethering/res/values-mcc310-mnc004-en-rGB/strings.xml new file mode 100644 index 0000000000..45647f93f2 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-en-rGB/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tethering has no Internet" + "Devices can’t connect" + "Turn off tethering" + "Hotspot or tethering is on" + "Additional charges may apply while roaming" + diff --git a/Tethering/res/values-mcc310-mnc004-en-rIN/strings.xml b/Tethering/res/values-mcc310-mnc004-en-rIN/strings.xml new file mode 100644 index 0000000000..45647f93f2 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-en-rIN/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tethering has no Internet" + "Devices can’t connect" + "Turn off tethering" + "Hotspot or tethering is on" + "Additional charges may apply while roaming" + diff --git a/Tethering/res/values-mcc310-mnc004-en-rXC/strings.xml b/Tethering/res/values-mcc310-mnc004-en-rXC/strings.xml new file mode 100644 index 0000000000..7877074afc --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-en-rXC/strings.xml @@ -0,0 +1,24 @@ + + + + + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‏‎‎‏‏‏‎‎‏‎‎‏‏‎‏‎‎‎‏‎‏‎‏‏‏‏‏‎‎‏‏‎‎‎‎‏‎‏‏‏‏‎‏‎‏‎‎‎‏‏‏‎‏‎‎‎Tethering has no internet‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‎‏‎‏‎‏‏‏‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‎‎‎‎‏‏‏‎‎‏‏‎‏‏‎‎‏‎‏‎‎‎‎‏‏‏‎Devices can’t connect‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‎‎‏‏‏‎‏‏‏‎‏‎‎‏‎‏‎‏‎‎‏‏‏‎‎‎‏‏‎‎‏‏‎‏‎‏‏‏‏‎‎‎‏‎‏‏‎‎‎‏‎‏‎‎‎‎Turn off tethering‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‏‏‏‎‏‏‎‏‎‏‎‏‎‏‏‎‎‎‎‏‎‎‎‏‏‎‎‎‎‎‎‎‎‏‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‎‎‏‏‏‎Hotspot or tethering is on‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‎‏‎‎‎‏‏‏‏‏‏‏‎‏‏‎‏‏‏‏‎‎‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‎‎‏‏‏‏‏‎‏‎‏‎‏‏‏‏‎‎Additional charges may apply while roaming‎‏‎‎‏‎" + diff --git a/Tethering/res/values-mcc310-mnc004-es-rUS/strings.xml b/Tethering/res/values-mcc310-mnc004-es-rUS/strings.xml new file mode 100644 index 0000000000..08edd81a6b --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-es-rUS/strings.xml @@ -0,0 +1,24 @@ + + + + + "La conexión mediante dispositivo móvil no tiene Internet" + "No se pueden conectar los dispositivos" + "Desactivar conexión mediante dispositivo móvil" + "Se activó el hotspot o la conexión mediante dispositivo móvil" + "Es posible que se apliquen cargos adicionales por roaming" + diff --git a/Tethering/res/values-mcc310-mnc004-es/strings.xml b/Tethering/res/values-mcc310-mnc004-es/strings.xml new file mode 100644 index 0000000000..79f51d00e2 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-es/strings.xml @@ -0,0 +1,24 @@ + + + + + "La conexión no se puede compartir, porque no hay acceso a Internet" + "Los dispositivos no se pueden conectar" + "Desactivar conexión compartida" + "Punto de acceso o conexión compartida activados" + "Puede que se apliquen cargos adicionales en itinerancia" + diff --git a/Tethering/res/values-mcc310-mnc004-et/strings.xml b/Tethering/res/values-mcc310-mnc004-et/strings.xml new file mode 100644 index 0000000000..2da5f8a6d6 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-et/strings.xml @@ -0,0 +1,24 @@ + + + + + "Jagamisel puudub internetiühendus" + "Seadmed ei saa ühendust luua" + "Lülita jagamine välja" + "Kuumkoht või jagamine on sisse lülitatud" + "Rändluse kasutamisega võivad kaasneda lisatasud" + diff --git a/Tethering/res/values-mcc310-mnc004-eu/strings.xml b/Tethering/res/values-mcc310-mnc004-eu/strings.xml new file mode 100644 index 0000000000..2073f2806c --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-eu/strings.xml @@ -0,0 +1,24 @@ + + + + + "Konexioa partekatzeko aukerak ez du Interneteko konexiorik" + "Ezin dira konektatu gailuak" + "Desaktibatu konexioa partekatzeko aukera" + "Wifi-gunea edo konexioa partekatzeko aukera aktibatuta dago" + "Baliteke kostu gehigarriak ordaindu behar izatea ibiltaritzan" + diff --git a/Tethering/res/values-mcc310-mnc004-fa/strings.xml b/Tethering/res/values-mcc310-mnc004-fa/strings.xml new file mode 100644 index 0000000000..e21b2a0852 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-fa/strings.xml @@ -0,0 +1,24 @@ + + + + + "«اشتراک‌گذاری اینترنت» به اینترنت دسترسی ندارد" + "دستگاه‌ها متصل نمی‌شوند" + "خاموش کردن «اشتراک‌گذاری اینترنت»" + "«نقطه اتصال» یا «اشتراک‌گذاری اینترنت» روشن است" + "ممکن است درحین فراگردی تغییرات دیگر اعمال شود" + diff --git a/Tethering/res/values-mcc310-mnc004-fi/strings.xml b/Tethering/res/values-mcc310-mnc004-fi/strings.xml new file mode 100644 index 0000000000..88b0b13eb4 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-fi/strings.xml @@ -0,0 +1,24 @@ + + + + + "Ei jaettavaa internetyhteyttä" + "Laitteet eivät voi muodostaa yhteyttä" + "Laita yhteyden jakaminen pois päältä" + "Hotspot tai yhteyden jakaminen on päällä" + "Roaming voi aiheuttaa lisämaksuja" + diff --git a/Tethering/res/values-mcc310-mnc004-fr-rCA/strings.xml b/Tethering/res/values-mcc310-mnc004-fr-rCA/strings.xml new file mode 100644 index 0000000000..3b781bc8db --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-fr-rCA/strings.xml @@ -0,0 +1,24 @@ + + + + + "Le partage de connexion n\'est pas connecté à Internet" + "Impossible de connecter les appareils" + "Désactiver le partage de connexion" + "Le point d\'accès ou le partage de connexion est activé" + "En itinérance, des frais supplémentaires peuvent s\'appliquer" + diff --git a/Tethering/res/values-mcc310-mnc004-fr/strings.xml b/Tethering/res/values-mcc310-mnc004-fr/strings.xml new file mode 100644 index 0000000000..51d7203c36 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-fr/strings.xml @@ -0,0 +1,24 @@ + + + + + "Aucune connexion à Internet n\'est disponible pour le partage de connexion" + "Impossible de connecter les appareils" + "Désactiver le partage de connexion" + "Le point d\'accès ou le partage de connexion est activé" + "En itinérance, des frais supplémentaires peuvent s\'appliquer" + diff --git a/Tethering/res/values-mcc310-mnc004-gl/strings.xml b/Tethering/res/values-mcc310-mnc004-gl/strings.xml new file mode 100644 index 0000000000..008ccb475d --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-gl/strings.xml @@ -0,0 +1,24 @@ + + + + + "A conexión compartida non ten Internet" + "Non se puideron conectar os dispositivos" + "Desactivar conexión compartida" + "Está activada a zona wifi ou a conexión compartida" + "Pódense aplicar cargos adicionais en itinerancia" + diff --git a/Tethering/res/values-mcc310-mnc004-gu/strings.xml b/Tethering/res/values-mcc310-mnc004-gu/strings.xml new file mode 100644 index 0000000000..f2e3b4df78 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-gu/strings.xml @@ -0,0 +1,24 @@ + + + + + "ઇન્ટરનેટ શેર કરવાની સુવિધામાં ઇન્ટરનેટ નથી" + "ડિવાઇસ કનેક્ટ કરી શકાતા નથી" + "ઇન્ટરનેટ શેર કરવાની સુવિધા બંધ કરો" + "હૉટસ્પૉટ અથવા ઇન્ટરનેટ શેર કરવાની સુવિધા ચાલુ છે" + "રોમિંગમાં વધારાના શુલ્ક લાગી શકે છે" + diff --git a/Tethering/res/values-mcc310-mnc004-hi/strings.xml b/Tethering/res/values-mcc310-mnc004-hi/strings.xml new file mode 100644 index 0000000000..b11839d760 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-hi/strings.xml @@ -0,0 +1,24 @@ + + + + + "टेदरिंग से इंटरनेट नहीं चल रहा" + "डिवाइस कनेक्ट नहीं हो पा रहे" + "टेदरिंग बंद करें" + "हॉटस्पॉट या टेदरिंग चालू है" + "रोमिंग के दौरान अतिरिक्त शुल्क लग सकता है" + diff --git a/Tethering/res/values-mcc310-mnc004-hr/strings.xml b/Tethering/res/values-mcc310-mnc004-hr/strings.xml new file mode 100644 index 0000000000..0a5aca25b1 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-hr/strings.xml @@ -0,0 +1,24 @@ + + + + + "Modemsko povezivanje nema internet" + "Uređaji se ne mogu povezati" + "Isključivanje modemskog povezivanja" + "Uključena je žarišna točka ili modemsko povezivanje" + "U roamingu su mogući dodatni troškovi" + diff --git a/Tethering/res/values-mcc310-mnc004-hu/strings.xml b/Tethering/res/values-mcc310-mnc004-hu/strings.xml new file mode 100644 index 0000000000..21c689a44e --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-hu/strings.xml @@ -0,0 +1,24 @@ + + + + + "Nincs internetkapcsolat az internet megosztásához" + "Az eszközök nem tudnak csatlakozni" + "Internetmegosztás kikapcsolása" + "A hotspot vagy az internetmegosztás be van kapcsolva" + "Roaming során további díjak léphetnek fel" + diff --git a/Tethering/res/values-mcc310-mnc004-hy/strings.xml b/Tethering/res/values-mcc310-mnc004-hy/strings.xml new file mode 100644 index 0000000000..689d92870e --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-hy/strings.xml @@ -0,0 +1,24 @@ + + + + + "Մոդեմի ռեժիմի կապը բացակայում է" + "Չհաջողվեց միացնել սարքը" + "Անջատել մոդեմի ռեժիմը" + "Թեժ կետը կամ մոդեմի ռեժիմը միացված է" + "Ռոումինգում կարող են լրացուցիչ վճարներ գանձվել" + diff --git a/Tethering/res/values-mcc310-mnc004-in/strings.xml b/Tethering/res/values-mcc310-mnc004-in/strings.xml new file mode 100644 index 0000000000..a5f4d19abf --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-in/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tidak ada koneksi internet di tethering" + "Perangkat tidak dapat terhubung" + "Nonaktifkan tethering" + "Hotspot atau tethering aktif" + "Biaya tambahan mungkin berlaku saat roaming" + diff --git a/Tethering/res/values-mcc310-mnc004-is/strings.xml b/Tethering/res/values-mcc310-mnc004-is/strings.xml new file mode 100644 index 0000000000..fc7e8aaf4e --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-is/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tjóðrun er ekki með internettengingu" + "Tæki geta ekki tengst" + "Slökkva á tjóðrun" + "Kveikt er á heitum reit eða tjóðrun" + "Viðbótargjöld kunna að eiga við í reiki" + diff --git a/Tethering/res/values-mcc310-mnc004-it/strings.xml b/Tethering/res/values-mcc310-mnc004-it/strings.xml new file mode 100644 index 0000000000..6456dd1b80 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-it/strings.xml @@ -0,0 +1,24 @@ + + + + + "Nessuna connessione a Internet per il tethering" + "Impossibile connettere i dispositivi" + "Disattiva il tethering" + "Hotspot o tethering attivi" + "Potrebbero essere applicati costi aggiuntivi durante il roaming" + diff --git a/Tethering/res/values-mcc310-mnc004-iw/strings.xml b/Tethering/res/values-mcc310-mnc004-iw/strings.xml new file mode 100644 index 0000000000..46b24bd3c5 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-iw/strings.xml @@ -0,0 +1,24 @@ + + + + + "אי אפשר להפעיל את תכונת שיתוף האינטרנט בין מכשירים כי אין חיבור לאינטרנט" + "למכשירים אין אפשרות להתחבר" + "השבתה של שיתוף האינטרנט בין מכשירים" + "תכונת הנקודה לשיתוף אינטרנט או תכונת שיתוף האינטרנט בין מכשירים פועלת" + "ייתכנו חיובים נוספים בעת נדידה" + diff --git a/Tethering/res/values-mcc310-mnc004-ja/strings.xml b/Tethering/res/values-mcc310-mnc004-ja/strings.xml new file mode 100644 index 0000000000..e6eb277b90 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-ja/strings.xml @@ -0,0 +1,24 @@ + + + + + "テザリングがインターネットに接続されていません" + "デバイスを接続できません" + "テザリングを OFF にする" + "アクセス ポイントまたはテザリングが ON です" + "ローミング時に追加料金が発生することがあります" + diff --git a/Tethering/res/values-mcc310-mnc004-ka/strings.xml b/Tethering/res/values-mcc310-mnc004-ka/strings.xml new file mode 100644 index 0000000000..aeddd7101d --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-ka/strings.xml @@ -0,0 +1,24 @@ + + + + + "ტეტერინგს არ აქვს ინტერნეტზე წვდომა" + "მოწყობილობები ვერ ახერხებენ დაკავშირებას" + "ტეტერინგის გამორთვა" + "ჩართულია უსადენო ქსელი ან ტეტერინგი" + "როუმინგის გამოყენებისას შეიძლება ჩამოგეჭრათ დამატებითი საფასური" + diff --git a/Tethering/res/values-mcc310-mnc004-kk/strings.xml b/Tethering/res/values-mcc310-mnc004-kk/strings.xml new file mode 100644 index 0000000000..255f0a276f --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-kk/strings.xml @@ -0,0 +1,24 @@ + + + + + "Тетеринг режимі интернет байланысынсыз пайдаланылуда" + "Құрылғыларды байланыстыру мүмкін емес" + "Тетерингіні өшіру" + "Хотспот немесе тетеринг қосулы" + "Роуминг кезінде қосымша ақы алынуы мүмкін." + diff --git a/Tethering/res/values-mcc310-mnc004-km/strings.xml b/Tethering/res/values-mcc310-mnc004-km/strings.xml new file mode 100644 index 0000000000..2bceb1cf77 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-km/strings.xml @@ -0,0 +1,24 @@ + + + + + "ការភ្ជាប់​មិនមានអ៊ីនធឺណិត​ទេ" + "មិនអាច​ភ្ជាប់ឧបករណ៍​បានទេ" + "បិទការភ្ជាប់" + "ហតស្ប៉ត ឬការភ្ជាប់​ត្រូវបានបើក" + "អាចមាន​ការគិតថ្លៃ​បន្ថែម នៅពេល​រ៉ូមីង" + diff --git a/Tethering/res/values-mcc310-mnc004-kn/strings.xml b/Tethering/res/values-mcc310-mnc004-kn/strings.xml new file mode 100644 index 0000000000..ed769305a6 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-kn/strings.xml @@ -0,0 +1,24 @@ + + + + + "ಟೆಥರಿಂಗ್‌ ಯಾವುದೇ ಇಂಟರ್ನೆಟ್ ಕನೆಕ್ಷನ್ ಹೊಂದಿಲ್ಲ" + "ಸಾಧನಗಳನ್ನು ಕನೆಕ್ಟ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ" + "ಟೆಥರಿಂಗ್‌ ಆಫ್ ಮಾಡಿ" + "ಹಾಟ್‌ಸ್ಪಾಟ್ ಅಥವಾ ಟೆಥರಿಂಗ್‌ ಆನ್ ಆಗಿದೆ" + "ರೋಮಿಂಗ್‌ನಲ್ಲಿರುವಾಗ ಹೆಚ್ಚುವರಿ ಶುಲ್ಕಗಳು ಅನ್ವಯವಾಗಬಹುದು" + diff --git a/Tethering/res/values-mcc310-mnc004-ko/strings.xml b/Tethering/res/values-mcc310-mnc004-ko/strings.xml new file mode 100644 index 0000000000..6e504941eb --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-ko/strings.xml @@ -0,0 +1,24 @@ + + + + + "테더링으로 인터넷을 사용할 수 없음" + "기기에서 연결할 수 없음" + "테더링 사용 중지" + "핫스팟 또는 테더링 켜짐" + "로밍 중에는 추가 요금이 발생할 수 있습니다." + diff --git a/Tethering/res/values-mcc310-mnc004-ky/strings.xml b/Tethering/res/values-mcc310-mnc004-ky/strings.xml new file mode 100644 index 0000000000..d68128b9a5 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-ky/strings.xml @@ -0,0 +1,24 @@ + + + + + "Модем режими Интернети жок колдонулууда" + "Түзмөктөр туташпай жатат" + "Модем режимин өчүрүү" + "Байланыш түйүнү же модем режими күйүк" + "Роумингде кошумча акы алынышы мүмкүн" + diff --git a/Tethering/res/values-mcc310-mnc004-lo/strings.xml b/Tethering/res/values-mcc310-mnc004-lo/strings.xml new file mode 100644 index 0000000000..03e134a0fc --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-lo/strings.xml @@ -0,0 +1,24 @@ + + + + + "ການປ່ອຍສັນຍານບໍ່ມີອິນເຕີເນັດ" + "ອຸປະກອນບໍ່ສາມາດເຊື່ອມຕໍ່ໄດ້" + "ປິດການປ່ອຍສັນຍານ" + "ເປີດໃຊ້ຮັອດສະປອດ ຫຼື ການປ່ອຍສັນຍານຢູ່" + "ອາດມີຄ່າໃຊ້ຈ່າຍເພີ່ມເຕີມໃນລະຫວ່າງການໂຣມມິງ" + diff --git a/Tethering/res/values-mcc310-mnc004-lt/strings.xml b/Tethering/res/values-mcc310-mnc004-lt/strings.xml new file mode 100644 index 0000000000..652cedc6e6 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-lt/strings.xml @@ -0,0 +1,24 @@ + + + + + "Nėra įrenginio kaip modemo naudojimo interneto ryšio" + "Nepavyko susieti įrenginių" + "Išjungti įrenginio kaip modemo naudojimą" + "Įjungtas viešosios interneto prieigos taškas arba įrenginio kaip modemo naudojimas" + "Veikiant tarptinkliniam ryšiui gali būti taikomi papildomi mokesčiai" + diff --git a/Tethering/res/values-mcc310-mnc004-lv/strings.xml b/Tethering/res/values-mcc310-mnc004-lv/strings.xml new file mode 100644 index 0000000000..221972298c --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-lv/strings.xml @@ -0,0 +1,24 @@ + + + + + "Piesaistei nav interneta savienojuma" + "Nevar savienot ierīces" + "Izslēgt piesaisti" + "Ir ieslēgts tīklājs vai piesaiste" + "Viesabonēšanas laikā var tikt piemērota papildu samaksa" + diff --git a/Tethering/res/values-mcc310-mnc004-mk/strings.xml b/Tethering/res/values-mcc310-mnc004-mk/strings.xml new file mode 100644 index 0000000000..227f9e3466 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-mk/strings.xml @@ -0,0 +1,24 @@ + + + + + "Нема интернет преку мобилен" + "Уредите не може да се поврзат" + "Исклучи интернет преку мобилен" + "Точката на пристап или интернетот преку мобилен е вклучен" + "При роаминг може да се наплатат дополнителни трошоци" + diff --git a/Tethering/res/values-mcc310-mnc004-ml/strings.xml b/Tethering/res/values-mcc310-mnc004-ml/strings.xml new file mode 100644 index 0000000000..ec43885126 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-ml/strings.xml @@ -0,0 +1,24 @@ + + + + + "ടെതറിംഗിന് ഇന്റർനെറ്റ് ഇല്ല" + "ഉപകരണങ്ങൾ കണക്റ്റ് ചെയ്യാനാവില്ല" + "ടെതറിംഗ് ഓഫാക്കുക" + "ഹോട്ട്‌സ്‌പോട്ട് അല്ലെങ്കിൽ ടെതറിംഗ് ഓണാണ്" + "റോമിംഗ് ചെയ്യുമ്പോൾ അധിക നിരക്കുകൾ ബാധകമായേക്കാം" + diff --git a/Tethering/res/values-mcc310-mnc004-mn/strings.xml b/Tethering/res/values-mcc310-mnc004-mn/strings.xml new file mode 100644 index 0000000000..e263573799 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-mn/strings.xml @@ -0,0 +1,24 @@ + + + + + "Модемд интернэт алга байна" + "Төхөөрөмжүүд холбогдох боломжгүй байна" + "Модем болгохыг унтраах" + "Сүлжээний цэг эсвэл модем болгох асаалттай байна" + "Роумингийн үеэр нэмэлт төлбөр нэхэмжилж болзошгүй" + diff --git a/Tethering/res/values-mcc310-mnc004-mr/strings.xml b/Tethering/res/values-mcc310-mnc004-mr/strings.xml new file mode 100644 index 0000000000..adf845d078 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-mr/strings.xml @@ -0,0 +1,24 @@ + + + + + "टेदरिंगला इंटरनेट नाही" + "डिव्हाइस कनेक्ट होऊ शकत नाहीत" + "टेदरिंग बंद करा" + "हॉटस्पॉट किंवा टेदरिंग सुरू आहे" + "रोमिंगदरम्यान अतिरिक्त शुल्क लागू होऊ शकतात" + diff --git a/Tethering/res/values-mcc310-mnc004-ms/strings.xml b/Tethering/res/values-mcc310-mnc004-ms/strings.xml new file mode 100644 index 0000000000..f65c451e4c --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-ms/strings.xml @@ -0,0 +1,24 @@ + + + + + "Penambatan tiada Internet" + "Peranti tidak dapat disambungkan" + "Matikan penambatan" + "Tempat liputan atau penambatan dihidupkan" + "Caj tambahan mungkin digunakan semasa perayauan" + diff --git a/Tethering/res/values-mcc310-mnc004-my/strings.xml b/Tethering/res/values-mcc310-mnc004-my/strings.xml new file mode 100644 index 0000000000..4118e775cd --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-my/strings.xml @@ -0,0 +1,24 @@ + + + + + "မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်းတွင် အင်တာနက် မရှိပါ" + "စက်များ ချိတ်ဆက်၍ မရပါ" + "မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်း ပိတ်ရန်" + "ဟော့စပေါ့ (သို့) မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်း ဖွင့်ထားသည်" + "ပြင်ပကွန်ရက်နှင့် ချိတ်ဆက်သည့်အခါ နောက်ထပ်ကျသင့်မှုများ ရှိနိုင်သည်" + diff --git a/Tethering/res/values-mcc310-mnc004-nb/strings.xml b/Tethering/res/values-mcc310-mnc004-nb/strings.xml new file mode 100644 index 0000000000..36853583ce --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-nb/strings.xml @@ -0,0 +1,24 @@ + + + + + "Internettdeling har ikke internettilgang" + "Enhetene kan ikke koble til" + "Slå av internettdeling" + "Wi-Fi-sone eller internettdeling er på" + "Ytterligere kostnader kan påløpe under roaming" + diff --git a/Tethering/res/values-mcc310-mnc004-ne/strings.xml b/Tethering/res/values-mcc310-mnc004-ne/strings.xml new file mode 100644 index 0000000000..d074f15699 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-ne/strings.xml @@ -0,0 +1,24 @@ + + + + + "टेदरिङमार्फत इन्टरनेट कनेक्सन प्राप्त हुन सकेन" + "यन्त्रहरू कनेक्ट गर्न सकिएन" + "टेदरिङ निष्क्रिय पार्नुहोस्" + "हटस्पट वा टेदरिङ सक्रिय छ" + "रोमिङ सेवा प्रयोग गर्दा अतिरिक्त शुल्क लाग्न सक्छ" + diff --git a/Tethering/res/values-mcc310-mnc004-nl/strings.xml b/Tethering/res/values-mcc310-mnc004-nl/strings.xml new file mode 100644 index 0000000000..1d888942f4 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-nl/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tethering heeft geen internet" + "Apparaten kunnen niet worden verbonden" + "Tethering uitschakelen" + "Hotspot of tethering is ingeschakeld" + "Er kunnen extra kosten voor roaming in rekening worden gebracht." + diff --git a/Tethering/res/values-mcc310-mnc004-or/strings.xml b/Tethering/res/values-mcc310-mnc004-or/strings.xml new file mode 100644 index 0000000000..8038815fe8 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-or/strings.xml @@ -0,0 +1,24 @@ + + + + + "ଟିଥରିଂ ପାଇଁ କୌଣସି ଇଣ୍ଟର୍ନେଟ୍ ସଂଯୋଗ ନାହିଁ" + "ଡିଭାଇସଗୁଡ଼ିକ ସଂଯୋଗ କରାଯାଇପାରିବ ନାହିଁ" + "ଟିଥରିଂ ବନ୍ଦ କରନ୍ତୁ" + "ହଟସ୍ପଟ୍ କିମ୍ବା ଟିଥରିଂ ଚାଲୁ ଅଛି" + "ରୋମିଂରେ ଥିବା ସମୟରେ ଅତିରିକ୍ତ ଶୁଳ୍କ ଲାଗୁ ହୋଇପାରେ" + diff --git a/Tethering/res/values-mcc310-mnc004-pa/strings.xml b/Tethering/res/values-mcc310-mnc004-pa/strings.xml new file mode 100644 index 0000000000..819833eab0 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-pa/strings.xml @@ -0,0 +1,24 @@ + + + + + "ਟੈਦਰਿੰਗ ਕੋਲ ਇੰਟਰਨੈੱਟ ਪਹੁੰਚ ਨਹੀਂ ਹੈ" + "ਡੀਵਾਈਸ ਕਨੈਕਟ ਨਹੀਂ ਕੀਤੇ ਜਾ ਸਕਦੇ" + "ਟੈਦਰਿੰਗ ਬੰਦ ਕਰੋ" + "ਹੌਟਸਪੌਟ ਜਾਂ ਟੈਦਰਿੰਗ ਚਾਲੂ ਹੈ" + "ਰੋਮਿੰਗ ਦੌਰਾਨ ਵਧੀਕ ਖਰਚੇ ਲਾਗੂ ਹੋ ਸਕਦੇ ਹਨ" + diff --git a/Tethering/res/values-mcc310-mnc004-pl/strings.xml b/Tethering/res/values-mcc310-mnc004-pl/strings.xml new file mode 100644 index 0000000000..65e4380e39 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-pl/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tethering nie ma internetu" + "Urządzenia nie mogą się połączyć" + "Wyłącz tethering" + "Hotspot lub tethering jest włączony" + "Podczas korzystania z roamingu mogą zostać naliczone dodatkowe opłaty" + diff --git a/Tethering/res/values-mcc310-mnc004-pt-rBR/strings.xml b/Tethering/res/values-mcc310-mnc004-pt-rBR/strings.xml new file mode 100644 index 0000000000..d8866170c1 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-pt-rBR/strings.xml @@ -0,0 +1,24 @@ + + + + + "O tethering não tem Internet" + "Não é possível conectar os dispositivos" + "Desativar o tethering" + "Ponto de acesso ou tethering ativado" + "Pode haver cobranças extras durante o roaming" + diff --git a/Tethering/res/values-mcc310-mnc004-pt-rPT/strings.xml b/Tethering/res/values-mcc310-mnc004-pt-rPT/strings.xml new file mode 100644 index 0000000000..bfd45ca0a3 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-pt-rPT/strings.xml @@ -0,0 +1,24 @@ + + + + + "A ligação (à Internet) via telemóvel não tem Internet" + "Não é possível ligar os dispositivos" + "Desativar ligação (à Internet) via telemóvel" + "A zona Wi-Fi ou a ligação (à Internet) via telemóvel está ativada" + "Podem aplicar-se custos adicionais em roaming." + diff --git a/Tethering/res/values-mcc310-mnc004-pt/strings.xml b/Tethering/res/values-mcc310-mnc004-pt/strings.xml new file mode 100644 index 0000000000..d8866170c1 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-pt/strings.xml @@ -0,0 +1,24 @@ + + + + + "O tethering não tem Internet" + "Não é possível conectar os dispositivos" + "Desativar o tethering" + "Ponto de acesso ou tethering ativado" + "Pode haver cobranças extras durante o roaming" + diff --git a/Tethering/res/values-mcc310-mnc004-ro/strings.xml b/Tethering/res/values-mcc310-mnc004-ro/strings.xml new file mode 100644 index 0000000000..8d87a9e516 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-ro/strings.xml @@ -0,0 +1,24 @@ + + + + + "Procesul de tethering nu are internet" + "Dispozitivele nu se pot conecta" + "Dezactivați procesul de tethering" + "S-a activat hotspotul sau tethering" + "Se pot aplica taxe suplimentare pentru roaming" + diff --git a/Tethering/res/values-mcc310-mnc004-ru/strings.xml b/Tethering/res/values-mcc310-mnc004-ru/strings.xml new file mode 100644 index 0000000000..dbdb9ebe49 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-ru/strings.xml @@ -0,0 +1,24 @@ + + + + + "Режим модема используется без доступа к Интернету" + "Невозможно подключить устройства." + "Отключить режим модема" + "Включены точка доступа или режим модема" + "За использование услуг связи в роуминге может взиматься дополнительная плата." + diff --git a/Tethering/res/values-mcc310-mnc004-si/strings.xml b/Tethering/res/values-mcc310-mnc004-si/strings.xml new file mode 100644 index 0000000000..d8301e41c2 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-si/strings.xml @@ -0,0 +1,24 @@ + + + + + "ටෙදරින් හට අන්තර්ජාලය නැත" + "උපාංගවලට සම්බන්ධ විය නොහැකිය" + "ටෙදරින් ක්‍රියාවිරහිත කරන්න" + "හොට්ස්පොට් හෝ ටෙදරින් ක්‍රියාත්මකයි" + "රෝමිං අතරතුර අමතර ගාස්තු අදාළ විය හැකිය" + diff --git a/Tethering/res/values-mcc310-mnc004-sk/strings.xml b/Tethering/res/values-mcc310-mnc004-sk/strings.xml new file mode 100644 index 0000000000..bef71363f4 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-sk/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tethering nemá internetové pripojenie" + "Zariadenia sa nemôžu pripojiť" + "Vypnúť tethering" + "Je zapnutý hotspot alebo tethering" + "Počas roamingu vám môžu byť účtované ďalšie poplatky" + diff --git a/Tethering/res/values-mcc310-mnc004-sl/strings.xml b/Tethering/res/values-mcc310-mnc004-sl/strings.xml new file mode 100644 index 0000000000..3202c62e8a --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-sl/strings.xml @@ -0,0 +1,24 @@ + + + + + "Internetna povezava prek mobilnega telefona ni vzpostavljena" + "Napravi se ne moreta povezati" + "Izklopi internetno povezavo prek mobilnega telefona" + "Dostopna točka ali internetna povezava prek mobilnega telefona je vklopljena" + "Med gostovanjem lahko nastanejo dodatni stroški" + diff --git a/Tethering/res/values-mcc310-mnc004-sq/strings.xml b/Tethering/res/values-mcc310-mnc004-sq/strings.xml new file mode 100644 index 0000000000..37f6ad2868 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-sq/strings.xml @@ -0,0 +1,24 @@ + + + + + "Ndarja e internetit nuk ka internet" + "Pajisjet nuk mund të lidhen" + "Çaktivizo ndarjen e internetit" + "Zona e qasjes për internet ose ndarja e internetit është aktive" + "Mund të zbatohen tarifime shtesë kur je në roaming" + diff --git a/Tethering/res/values-mcc310-mnc004-sr/strings.xml b/Tethering/res/values-mcc310-mnc004-sr/strings.xml new file mode 100644 index 0000000000..5566d03ed1 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-sr/strings.xml @@ -0,0 +1,24 @@ + + + + + "Привезивање нема приступ интернету" + "Повезивање уређаја није успело" + "Искључи привезивање" + "Укључен је хотспот или привезивање" + "Можда важе додатни трошкови у ромингу" + diff --git a/Tethering/res/values-mcc310-mnc004-sv/strings.xml b/Tethering/res/values-mcc310-mnc004-sv/strings.xml new file mode 100644 index 0000000000..9765acd0cf --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-sv/strings.xml @@ -0,0 +1,24 @@ + + + + + "Det finns ingen internetanslutning för internetdelningen" + "Enheterna kan inte anslutas" + "Inaktivera internetdelning" + "Surfzon eller internetdelning har aktiverats" + "Ytterligare avgifter kan tillkomma vid roaming" + diff --git a/Tethering/res/values-mcc310-mnc004-sw/strings.xml b/Tethering/res/values-mcc310-mnc004-sw/strings.xml new file mode 100644 index 0000000000..cf850c9cd2 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-sw/strings.xml @@ -0,0 +1,24 @@ + + + + + "Kipengele cha kusambaza mtandao hakina intaneti" + "Imeshindwa kuunganisha vifaa" + "Zima kipengele cha kusambaza mtandao" + "Umewasha kipengele cha kusambaza mtandao au mtandao pepe" + "Huenda ukatozwa gharama za ziada ukitumia mitandao ya ng\'ambo" + diff --git a/Tethering/res/values-mcc310-mnc004-ta/strings.xml b/Tethering/res/values-mcc310-mnc004-ta/strings.xml new file mode 100644 index 0000000000..f4b15aab19 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-ta/strings.xml @@ -0,0 +1,24 @@ + + + + + "இணைப்பு முறைக்கு இணைய இணைப்பு இல்லை" + "சாதனங்களால் இணைய முடியவில்லை" + "இணைப்பு முறையை ஆஃப் செய்" + "ஹாட்ஸ்பாட் அல்லது இணைப்பு முறை ஆன் செய்யப்பட்டுள்ளது" + "ரோமிங்கின்போது கூடுதல் கட்டணங்கள் விதிக்கப்படக்கூடும்" + diff --git a/Tethering/res/values-mcc310-mnc004-te/strings.xml b/Tethering/res/values-mcc310-mnc004-te/strings.xml new file mode 100644 index 0000000000..937d34d520 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-te/strings.xml @@ -0,0 +1,24 @@ + + + + + "టెథరింగ్ చేయడానికి ఇంటర్నెట్ కనెక్షన్ లేదు" + "పరికరాలు కనెక్ట్ అవ్వడం లేదు" + "టెథరింగ్‌ను ఆఫ్ చేయండి" + "హాట్‌స్పాట్ లేదా టెథరింగ్ ఆన్‌లో ఉంది" + "రోమింగ్‌లో ఉన్నప్పుడు అదనపు ఛార్జీలు వర్తించవచ్చు" + diff --git a/Tethering/res/values-mcc310-mnc004-th/strings.xml b/Tethering/res/values-mcc310-mnc004-th/strings.xml new file mode 100644 index 0000000000..f781fae525 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-th/strings.xml @@ -0,0 +1,24 @@ + + + + + "การเชื่อมต่ออินเทอร์เน็ตผ่านมือถือไม่มีอินเทอร์เน็ต" + "อุปกรณ์เชื่อมต่อไม่ได้" + "ปิดการเชื่อมต่ออินเทอร์เน็ตผ่านมือถือ" + "ฮอตสปอตหรือการเชื่อมต่ออินเทอร์เน็ตผ่านมือถือเปิดอยู่" + "อาจมีค่าใช้จ่ายเพิ่มเติมขณะโรมมิ่ง" + diff --git a/Tethering/res/values-mcc310-mnc004-tl/strings.xml b/Tethering/res/values-mcc310-mnc004-tl/strings.xml new file mode 100644 index 0000000000..8d5d465373 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-tl/strings.xml @@ -0,0 +1,24 @@ + + + + + "Walang internet ang pag-tether" + "Hindi makakonekta ang mga device" + "I-off ang pag-tether" + "Naka-on ang Hotspot o pag-tether" + "Posibleng magkaroon ng mga karagdagang singil habang nagro-roam" + diff --git a/Tethering/res/values-mcc310-mnc004-tr/strings.xml b/Tethering/res/values-mcc310-mnc004-tr/strings.xml new file mode 100644 index 0000000000..80cab33ac0 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-tr/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tethering\'in internet bağlantısı yok" + "Cihazlar bağlanamıyor" + "Tethering\'i kapat" + "Hotspot veya tethering açık" + "Dolaşım sırasında ek ücretler uygulanabilir" + diff --git a/Tethering/res/values-mcc310-mnc004-uk/strings.xml b/Tethering/res/values-mcc310-mnc004-uk/strings.xml new file mode 100644 index 0000000000..c05932a5ae --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-uk/strings.xml @@ -0,0 +1,24 @@ + + + + + "Телефон, який використовується як модем, не підключений до Інтернету" + "Не вдається підключити пристрої" + "Вимкнути використання телефона як модема" + "Увімкнено точку доступу або використання телефона як модема" + "У роумінгу може стягуватися додаткова плата" + diff --git a/Tethering/res/values-mcc310-mnc004-ur/strings.xml b/Tethering/res/values-mcc310-mnc004-ur/strings.xml new file mode 100644 index 0000000000..d820eee8ba --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-ur/strings.xml @@ -0,0 +1,24 @@ + + + + + "ٹیدرنگ میں انٹرنیٹ نہیں ہے" + "آلات منسلک نہیں ہو سکتے" + "ٹیدرنگ آف کریں" + "ہاٹ اسپاٹ یا ٹیدرنگ آن ہے" + "رومنگ کے دوران اضافی چارجز لاگو ہو سکتے ہیں" + diff --git a/Tethering/res/values-mcc310-mnc004-uz/strings.xml b/Tethering/res/values-mcc310-mnc004-uz/strings.xml new file mode 100644 index 0000000000..726148aaee --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-uz/strings.xml @@ -0,0 +1,24 @@ + + + + + "Modem internetga ulanmagan" + "Qurilmalar ulanmadi" + "Modem rejimini faolsizlantirish" + "Hotspot yoki modem rejimi yoniq" + "Rouming vaqtida qoʻshimcha haq olinishi mumkin" + diff --git a/Tethering/res/values-mcc310-mnc004-vi/strings.xml b/Tethering/res/values-mcc310-mnc004-vi/strings.xml new file mode 100644 index 0000000000..b7cb0456b6 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-vi/strings.xml @@ -0,0 +1,24 @@ + + + + + "Không có Internet để chia sẻ kết Internet" + "Các thiết bị không thể kết nối" + "Tắt tính năng chia sẻ Internet" + "Điểm phát sóng hoặc tính năng chia sẻ Internet đang bật" + "Bạn có thể mất thêm phí dữ liệu khi chuyển vùng" + diff --git a/Tethering/res/values-mcc310-mnc004-zh-rCN/strings.xml b/Tethering/res/values-mcc310-mnc004-zh-rCN/strings.xml new file mode 100644 index 0000000000..af91afff9a --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-zh-rCN/strings.xml @@ -0,0 +1,24 @@ + + + + + "共享网络未连接到互联网" + "设备无法连接" + "关闭网络共享" + "热点或网络共享已开启" + "漫游时可能会产生额外的费用" + diff --git a/Tethering/res/values-mcc310-mnc004-zh-rHK/strings.xml b/Tethering/res/values-mcc310-mnc004-zh-rHK/strings.xml new file mode 100644 index 0000000000..28e6b80c01 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-zh-rHK/strings.xml @@ -0,0 +1,24 @@ + + + + + "無法透過網絡共享連線至互聯網" + "裝置無法連接" + "關閉網絡共享" + "熱點或網絡共享已開啟" + "漫遊時可能需要支付額外費用" + diff --git a/Tethering/res/values-mcc310-mnc004-zh-rTW/strings.xml b/Tethering/res/values-mcc310-mnc004-zh-rTW/strings.xml new file mode 100644 index 0000000000..528a1e5292 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-zh-rTW/strings.xml @@ -0,0 +1,24 @@ + + + + + "無法透過網路共用連上網際網路" + "裝置無法連線" + "關閉網路共用" + "無線基地台或網路共用已開啟" + "使用漫遊服務可能須支付額外費用" + diff --git a/Tethering/res/values-mcc310-mnc004-zu/strings.xml b/Tethering/res/values-mcc310-mnc004-zu/strings.xml new file mode 100644 index 0000000000..11eb666219 --- /dev/null +++ b/Tethering/res/values-mcc310-mnc004-zu/strings.xml @@ -0,0 +1,24 @@ + + + + + "Ukusebenzisa ifoni njengemodemu akunayo i-inthanethi" + "Amadivayisi awakwazi ukuxhumeka" + "Vala ukusebenzisa ifoni njengemodemu" + "I-hotspot noma ukusebenzisa ifoni njengemodemu kuvuliwe" + "Kungaba nezinkokhelo ezengeziwe uma uzula" + diff --git a/Tethering/res/values-mcc311-mnc480-af/strings.xml b/Tethering/res/values-mcc311-mnc480-af/strings.xml new file mode 100644 index 0000000000..9bfa5317a9 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-af/strings.xml @@ -0,0 +1,24 @@ + + + + + "Verbinding het nie internet nie" + "Toestelle kan nie koppel nie" + "Skakel verbinding af" + "Warmkol of verbinding is aan" + "Bykomende heffings kan geld terwyl jy swerf" + diff --git a/Tethering/res/values-mcc311-mnc480-am/strings.xml b/Tethering/res/values-mcc311-mnc480-am/strings.xml new file mode 100644 index 0000000000..5949dfa776 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-am/strings.xml @@ -0,0 +1,24 @@ + + + + + "ማስተሳሰር ምንም በይነመረብ የለውም" + "መሣሪያዎችን ማገናኘት አይቻልም" + "ማስተሳሰርን አጥፋ" + "መገናኛ ነጥብ ወይም ማስተሳሰር በርቷል" + "በሚያንዣብብበት ጊዜ ተጨማሪ ክፍያዎች ተፈጻሚ ሊሆኑ ይችላሉ" + diff --git a/Tethering/res/values-mcc311-mnc480-ar/strings.xml b/Tethering/res/values-mcc311-mnc480-ar/strings.xml new file mode 100644 index 0000000000..8467f9b1f5 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-ar/strings.xml @@ -0,0 +1,24 @@ + + + + + "ما مِن اتصال بالإنترنت خلال التوصيل" + "تعذّر اتصال الأجهزة" + "إيقاف التوصيل" + "نقطة الاتصال أو التوصيل مفعّلان" + "قد يتم تطبيق رسوم إضافية أثناء التجوال." + diff --git a/Tethering/res/values-mcc311-mnc480-as/strings.xml b/Tethering/res/values-mcc311-mnc480-as/strings.xml new file mode 100644 index 0000000000..9776bd89da --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-as/strings.xml @@ -0,0 +1,24 @@ + + + + + "টে\'ডাৰিঙৰ ইণ্টাৰনেট নাই" + "ডিভাইচসমূহ সংযোগ কৰিব নোৱাৰি" + "টে\'ডাৰিং অফ কৰক" + "হটস্পট অথবা টে\'ডাৰিং অন আছে" + "ৰ\'মিঙত থাকিলে অতিৰিক্ত মাচুল প্ৰযোজ্য হ’ব পাৰে" + diff --git a/Tethering/res/values-mcc311-mnc480-az/strings.xml b/Tethering/res/values-mcc311-mnc480-az/strings.xml new file mode 100644 index 0000000000..e6d3eaf9f0 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-az/strings.xml @@ -0,0 +1,24 @@ + + + + + "Modemin internetə girişi yoxdur" + "Cihazları qoşmaq mümkün deyil" + "Modemi deaktiv edin" + "Hotspot və ya modem aktivdir" + "Rouminq zamanı əlavə ödənişlər tətbiq edilə bilər" + diff --git a/Tethering/res/values-mcc311-mnc480-b+sr+Latn/strings.xml b/Tethering/res/values-mcc311-mnc480-b+sr+Latn/strings.xml new file mode 100644 index 0000000000..4c8a1df8ee --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-b+sr+Latn/strings.xml @@ -0,0 +1,24 @@ + + + + + "Privezivanje nema pristup internetu" + "Povezivanje uređaja nije uspelo" + "Isključi privezivanje" + "Uključen je hotspot ili privezivanje" + "Možda važe dodatni troškovi u romingu" + diff --git a/Tethering/res/values-mcc311-mnc480-be/strings.xml b/Tethering/res/values-mcc311-mnc480-be/strings.xml new file mode 100644 index 0000000000..edfa41e1ff --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-be/strings.xml @@ -0,0 +1,24 @@ + + + + + "Рэжым мадэма выкарыстоўваецца без доступу да інтэрнэту" + "Не ўдалося падключыць прылады" + "Выключыць рэжым мадэма" + "Хот-спот або рэжым мадэма ўключаны" + "Пры выкарыстанні роўмінгу можа спаганяцца дадатковая плата" + diff --git a/Tethering/res/values-mcc311-mnc480-bg/strings.xml b/Tethering/res/values-mcc311-mnc480-bg/strings.xml new file mode 100644 index 0000000000..f56398196f --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-bg/strings.xml @@ -0,0 +1,24 @@ + + + + + "Тетърингът няма връзка с интернет" + "Устройствата не могат да установят връзка" + "Изключване на тетъринга" + "Точката за достъп или тетърингът са включени" + "Възможно е да ви бъдат начислени допълнителни такси при роуминг" + diff --git a/Tethering/res/values-mcc311-mnc480-bn/strings.xml b/Tethering/res/values-mcc311-mnc480-bn/strings.xml new file mode 100644 index 0000000000..d8ecd2e988 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-bn/strings.xml @@ -0,0 +1,24 @@ + + + + + "টিথারিং করার জন্য কোনও ইন্টারনেট কানেকশন নেই" + "ডিভাইস কানেক্ট করতে পারছে না" + "টিথারিং বন্ধ করুন" + "হটস্পট বা টিথারিং চালু আছে" + "রোমিংয়ের সময় অতিরিক্ত চার্জ করা হতে পারে" + diff --git a/Tethering/res/values-mcc311-mnc480-bs/strings.xml b/Tethering/res/values-mcc311-mnc480-bs/strings.xml new file mode 100644 index 0000000000..b85fd5e285 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-bs/strings.xml @@ -0,0 +1,24 @@ + + + + + "Povezivanje putem mobitela nema internet" + "Uređaji se ne mogu povezati" + "Isključi povezivanje putem mobitela" + "Pristupna tačka ili povezivanje putem mobitela je uključeno" + "Mogu nastati dodatni troškovi u romingu" + diff --git a/Tethering/res/values-mcc311-mnc480-ca/strings.xml b/Tethering/res/values-mcc311-mnc480-ca/strings.xml new file mode 100644 index 0000000000..a3572151be --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-ca/strings.xml @@ -0,0 +1,24 @@ + + + + + "La compartició de xarxa no té accés a Internet" + "No es poden connectar els dispositius" + "Desactiva la compartició de xarxa" + "S\'ha activat el punt d\'accés Wi‑Fi o la compartició de xarxa" + "És possible que s\'apliquin costos addicionals en itinerància" + diff --git a/Tethering/res/values-mcc311-mnc480-cs/strings.xml b/Tethering/res/values-mcc311-mnc480-cs/strings.xml new file mode 100644 index 0000000000..91196be9e5 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-cs/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tethering nemá připojení k internetu" + "Zařízení se nemůžou připojit" + "Vypnout tethering" + "Je zapnutý hotspot nebo tethering" + "Při roamingu mohou být účtovány dodatečné poplatky" + diff --git a/Tethering/res/values-mcc311-mnc480-da/strings.xml b/Tethering/res/values-mcc311-mnc480-da/strings.xml new file mode 100644 index 0000000000..196890011d --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-da/strings.xml @@ -0,0 +1,24 @@ + + + + + "Netdeling har ingen internetforbindelse" + "Enheder kan ikke oprette forbindelse" + "Deaktiver netdeling" + "Hotspot eller netdeling er aktiveret" + "Der opkræves muligvis yderligere gebyrer ved roaming" + diff --git a/Tethering/res/values-mcc311-mnc480-de/strings.xml b/Tethering/res/values-mcc311-mnc480-de/strings.xml new file mode 100644 index 0000000000..eb3f8c52c0 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-de/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tethering hat keinen Internetzugriff" + "Geräte können sich nicht verbinden" + "Tethering deaktivieren" + "Hotspot oder Tethering ist aktiviert" + "Für das Roaming können zusätzliche Gebühren anfallen" + diff --git a/Tethering/res/values-mcc311-mnc480-el/strings.xml b/Tethering/res/values-mcc311-mnc480-el/strings.xml new file mode 100644 index 0000000000..56c3d81b63 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-el/strings.xml @@ -0,0 +1,24 @@ + + + + + "Η σύνδεση δεν έχει πρόσβαση στο διαδίκτυο" + "Δεν είναι δυνατή η σύνδεση των συσκευών" + "Απενεργοποιήστε τη σύνδεση" + "Ενεργό σημείο πρόσβασης Wi-Fi ή ενεργή σύνδεση" + "Ενδέχεται να ισχύουν επιπλέον χρεώσεις κατά την περιαγωγή." + diff --git a/Tethering/res/values-mcc311-mnc480-en-rAU/strings.xml b/Tethering/res/values-mcc311-mnc480-en-rAU/strings.xml new file mode 100644 index 0000000000..dd1a1971cd --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-en-rAU/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tethering has no Internet" + "Devices can’t connect" + "Turn off tethering" + "Hotspot or tethering is on" + "Additional charges may apply while roaming" + diff --git a/Tethering/res/values-mcc311-mnc480-en-rCA/strings.xml b/Tethering/res/values-mcc311-mnc480-en-rCA/strings.xml new file mode 100644 index 0000000000..dd1a1971cd --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-en-rCA/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tethering has no Internet" + "Devices can’t connect" + "Turn off tethering" + "Hotspot or tethering is on" + "Additional charges may apply while roaming" + diff --git a/Tethering/res/values-mcc311-mnc480-en-rGB/strings.xml b/Tethering/res/values-mcc311-mnc480-en-rGB/strings.xml new file mode 100644 index 0000000000..dd1a1971cd --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-en-rGB/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tethering has no Internet" + "Devices can’t connect" + "Turn off tethering" + "Hotspot or tethering is on" + "Additional charges may apply while roaming" + diff --git a/Tethering/res/values-mcc311-mnc480-en-rIN/strings.xml b/Tethering/res/values-mcc311-mnc480-en-rIN/strings.xml new file mode 100644 index 0000000000..dd1a1971cd --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-en-rIN/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tethering has no Internet" + "Devices can’t connect" + "Turn off tethering" + "Hotspot or tethering is on" + "Additional charges may apply while roaming" + diff --git a/Tethering/res/values-mcc311-mnc480-en-rXC/strings.xml b/Tethering/res/values-mcc311-mnc480-en-rXC/strings.xml new file mode 100644 index 0000000000..d3347aae20 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-en-rXC/strings.xml @@ -0,0 +1,24 @@ + + + + + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‎‎‎‎‎‏‎‎‏‏‏‏‎‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‎‏‎‏‏‎‏‏‏‎‎‏‎‏‎‎‎‏‎‎‎Tethering has no internet‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‏‎‏‎‎‏‎‎‏‏‏‎‏‏‎‏‎‏‎‏‎‎‎‏‎‎‎‎‎‏‏‏‏‎‏‎‎‎‎‏‎‏‏‎‏‏‎‏‎‎‏‏‏‏‏‎Devices can’t connect‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‎‏‏‎‎‏‏‏‎‏‎‏‎‎‎‏‏‏‎‎‏‏‏‏‎‎‏‏‏‏‏‏‎‎‎‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‎Turn off tethering‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‎‏‏‏‎‎‏‎‎‏‎‏‎‏‏‏‎‏‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‏‎‏‎‎‎‏‎‎‎‎‏‏‎Hotspot or tethering is on‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‏‏‎‏‎‏‎‏‎‎‎‏‎‏‎‏‏‎‏‎‎‎‏‏‏‏‎‎‏‏‏‏‎‎‎‏‎‎‎‎‏‏‎‏‎‏‎‎‏‏‎‎‏‏‎Additional charges may apply while roaming‎‏‎‎‏‎" + diff --git a/Tethering/res/values-mcc311-mnc480-es-rUS/strings.xml b/Tethering/res/values-mcc311-mnc480-es-rUS/strings.xml new file mode 100644 index 0000000000..2f0504f07d --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-es-rUS/strings.xml @@ -0,0 +1,24 @@ + + + + + "La conexión mediante dispositivo móvil no tiene Internet" + "No se pueden conectar los dispositivos" + "Desactivar conexión mediante dispositivo móvil" + "Se activó el hotspot o la conexión mediante dispositivo móvil" + "Es posible que se apliquen cargos adicionales por roaming" + diff --git a/Tethering/res/values-mcc311-mnc480-es/strings.xml b/Tethering/res/values-mcc311-mnc480-es/strings.xml new file mode 100644 index 0000000000..2d8f882425 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-es/strings.xml @@ -0,0 +1,24 @@ + + + + + "La conexión no se puede compartir, porque no hay acceso a Internet" + "Los dispositivos no se pueden conectar" + "Desactivar conexión compartida" + "Punto de acceso o conexión compartida activados" + "Puede que se apliquen cargos adicionales en itinerancia" + diff --git a/Tethering/res/values-mcc311-mnc480-et/strings.xml b/Tethering/res/values-mcc311-mnc480-et/strings.xml new file mode 100644 index 0000000000..8493c47071 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-et/strings.xml @@ -0,0 +1,24 @@ + + + + + "Jagamisel puudub internetiühendus" + "Seadmed ei saa ühendust luua" + "Lülita jagamine välja" + "Kuumkoht või jagamine on sisse lülitatud" + "Rändluse kasutamisega võivad kaasneda lisatasud" + diff --git a/Tethering/res/values-mcc311-mnc480-eu/strings.xml b/Tethering/res/values-mcc311-mnc480-eu/strings.xml new file mode 100644 index 0000000000..33bccab3e8 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-eu/strings.xml @@ -0,0 +1,24 @@ + + + + + "Konexioa partekatzeko aukerak ez du Interneteko konexiorik" + "Ezin dira konektatu gailuak" + "Desaktibatu konexioa partekatzeko aukera" + "Wifi-gunea edo konexioa partekatzeko aukera aktibatuta dago" + "Baliteke kostu gehigarriak ordaindu behar izatea ibiltaritzan" + diff --git a/Tethering/res/values-mcc311-mnc480-fa/strings.xml b/Tethering/res/values-mcc311-mnc480-fa/strings.xml new file mode 100644 index 0000000000..cf8a0cc277 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-fa/strings.xml @@ -0,0 +1,24 @@ + + + + + "«اشتراک‌گذاری اینترنت» به اینترنت دسترسی ندارد" + "دستگاه‌ها متصل نمی‌شوند" + "خاموش کردن «اشتراک‌گذاری اینترنت»" + "«نقطه اتصال» یا «اشتراک‌گذاری اینترنت» روشن است" + "ممکن است درحین فراگردی تغییرات دیگر اعمال شود" + diff --git a/Tethering/res/values-mcc311-mnc480-fi/strings.xml b/Tethering/res/values-mcc311-mnc480-fi/strings.xml new file mode 100644 index 0000000000..6a3ab806db --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-fi/strings.xml @@ -0,0 +1,24 @@ + + + + + "Ei jaettavaa internetyhteyttä" + "Laitteet eivät voi muodostaa yhteyttä" + "Laita yhteyden jakaminen pois päältä" + "Hotspot tai yhteyden jakaminen on päällä" + "Roaming voi aiheuttaa lisämaksuja" + diff --git a/Tethering/res/values-mcc311-mnc480-fr-rCA/strings.xml b/Tethering/res/values-mcc311-mnc480-fr-rCA/strings.xml new file mode 100644 index 0000000000..ffb9bf6047 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-fr-rCA/strings.xml @@ -0,0 +1,24 @@ + + + + + "Le partage de connexion n\'est pas connecté à Internet" + "Impossible de connecter les appareils" + "Désactiver le partage de connexion" + "Le point d\'accès ou le partage de connexion est activé" + "En itinérance, des frais supplémentaires peuvent s\'appliquer" + diff --git a/Tethering/res/values-mcc311-mnc480-fr/strings.xml b/Tethering/res/values-mcc311-mnc480-fr/strings.xml new file mode 100644 index 0000000000..768bce3f0a --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-fr/strings.xml @@ -0,0 +1,24 @@ + + + + + "Aucune connexion à Internet n\'est disponible pour le partage de connexion" + "Impossible de connecter les appareils" + "Désactiver le partage de connexion" + "Le point d\'accès ou le partage de connexion est activé" + "En itinérance, des frais supplémentaires peuvent s\'appliquer" + diff --git a/Tethering/res/values-mcc311-mnc480-gl/strings.xml b/Tethering/res/values-mcc311-mnc480-gl/strings.xml new file mode 100644 index 0000000000..0c4195a7ca --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-gl/strings.xml @@ -0,0 +1,24 @@ + + + + + "A conexión compartida non ten Internet" + "Non se puideron conectar os dispositivos" + "Desactivar conexión compartida" + "Está activada a zona wifi ou a conexión compartida" + "Pódense aplicar cargos adicionais en itinerancia" + diff --git a/Tethering/res/values-mcc311-mnc480-gu/strings.xml b/Tethering/res/values-mcc311-mnc480-gu/strings.xml new file mode 100644 index 0000000000..e9d33a7db2 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-gu/strings.xml @@ -0,0 +1,24 @@ + + + + + "ઇન્ટરનેટ શેર કરવાની સુવિધામાં ઇન્ટરનેટ નથી" + "ડિવાઇસ કનેક્ટ કરી શકાતા નથી" + "ઇન્ટરનેટ શેર કરવાની સુવિધા બંધ કરો" + "હૉટસ્પૉટ અથવા ઇન્ટરનેટ શેર કરવાની સુવિધા ચાલુ છે" + "રોમિંગમાં વધારાના શુલ્ક લાગી શકે છે" + diff --git a/Tethering/res/values-mcc311-mnc480-hi/strings.xml b/Tethering/res/values-mcc311-mnc480-hi/strings.xml new file mode 100644 index 0000000000..aa418ac5d3 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-hi/strings.xml @@ -0,0 +1,24 @@ + + + + + "टेदरिंग से इंटरनेट नहीं चल रहा" + "डिवाइस कनेक्ट नहीं हो पा रहे" + "टेदरिंग बंद करें" + "हॉटस्पॉट या टेदरिंग चालू है" + "रोमिंग के दौरान अतिरिक्त शुल्क लग सकता है" + diff --git a/Tethering/res/values-mcc311-mnc480-hr/strings.xml b/Tethering/res/values-mcc311-mnc480-hr/strings.xml new file mode 100644 index 0000000000..51c524afbc --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-hr/strings.xml @@ -0,0 +1,24 @@ + + + + + "Modemsko povezivanje nema internet" + "Uređaji se ne mogu povezati" + "Isključivanje modemskog povezivanja" + "Uključena je žarišna točka ili modemsko povezivanje" + "U roamingu su mogući dodatni troškovi" + diff --git a/Tethering/res/values-mcc311-mnc480-hu/strings.xml b/Tethering/res/values-mcc311-mnc480-hu/strings.xml new file mode 100644 index 0000000000..164e45edd1 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-hu/strings.xml @@ -0,0 +1,24 @@ + + + + + "Nincs internetkapcsolat az internet megosztásához" + "Az eszközök nem tudnak csatlakozni" + "Internetmegosztás kikapcsolása" + "A hotspot vagy az internetmegosztás be van kapcsolva" + "Roaming során további díjak léphetnek fel" + diff --git a/Tethering/res/values-mcc311-mnc480-hy/strings.xml b/Tethering/res/values-mcc311-mnc480-hy/strings.xml new file mode 100644 index 0000000000..e76c0a4c80 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-hy/strings.xml @@ -0,0 +1,24 @@ + + + + + "Մոդեմի ռեժիմի կապը բացակայում է" + "Չհաջողվեց միացնել սարքը" + "Անջատել մոդեմի ռեժիմը" + "Թեժ կետը կամ մոդեմի ռեժիմը միացված է" + "Ռոումինգում կարող են լրացուցիչ վճարներ գանձվել" + diff --git a/Tethering/res/values-mcc311-mnc480-in/strings.xml b/Tethering/res/values-mcc311-mnc480-in/strings.xml new file mode 100644 index 0000000000..2b817f8abd --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-in/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tidak ada koneksi internet di tethering" + "Perangkat tidak dapat terhubung" + "Nonaktifkan tethering" + "Hotspot atau tethering aktif" + "Biaya tambahan mungkin berlaku saat roaming" + diff --git a/Tethering/res/values-mcc311-mnc480-is/strings.xml b/Tethering/res/values-mcc311-mnc480-is/strings.xml new file mode 100644 index 0000000000..a338d9c7ca --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-is/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tjóðrun er ekki með internettengingu" + "Tæki geta ekki tengst" + "Slökkva á tjóðrun" + "Kveikt er á heitum reit eða tjóðrun" + "Viðbótargjöld kunna að eiga við í reiki" + diff --git a/Tethering/res/values-mcc311-mnc480-it/strings.xml b/Tethering/res/values-mcc311-mnc480-it/strings.xml new file mode 100644 index 0000000000..77769c2ac5 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-it/strings.xml @@ -0,0 +1,24 @@ + + + + + "Nessuna connessione a Internet per il tethering" + "Impossibile connettere i dispositivi" + "Disattiva il tethering" + "Hotspot o tethering attivi" + "Potrebbero essere applicati costi aggiuntivi durante il roaming" + diff --git a/Tethering/res/values-mcc311-mnc480-iw/strings.xml b/Tethering/res/values-mcc311-mnc480-iw/strings.xml new file mode 100644 index 0000000000..5267b51264 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-iw/strings.xml @@ -0,0 +1,24 @@ + + + + + "אי אפשר להפעיל את תכונת שיתוף האינטרנט בין מכשירים כי אין חיבור לאינטרנט" + "למכשירים אין אפשרות להתחבר" + "השבתה של שיתוף האינטרנט בין מכשירים" + "תכונת הנקודה לשיתוף אינטרנט או תכונת שיתוף האינטרנט בין מכשירים פועלת" + "ייתכנו חיובים נוספים בעת נדידה" + diff --git a/Tethering/res/values-mcc311-mnc480-ja/strings.xml b/Tethering/res/values-mcc311-mnc480-ja/strings.xml new file mode 100644 index 0000000000..66a9a6dd35 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-ja/strings.xml @@ -0,0 +1,24 @@ + + + + + "テザリングがインターネットに接続されていません" + "デバイスを接続できません" + "テザリングを OFF にする" + "アクセス ポイントまたはテザリングが ON です" + "ローミング時に追加料金が発生することがあります" + diff --git a/Tethering/res/values-mcc311-mnc480-ka/strings.xml b/Tethering/res/values-mcc311-mnc480-ka/strings.xml new file mode 100644 index 0000000000..d8ad880849 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-ka/strings.xml @@ -0,0 +1,24 @@ + + + + + "ტეტერინგს არ აქვს ინტერნეტზე წვდომა" + "მოწყობილობები ვერ ახერხებენ დაკავშირებას" + "ტეტერინგის გამორთვა" + "ჩართულია უსადენო ქსელი ან ტეტერინგი" + "როუმინგის გამოყენებისას შეიძლება ჩამოგეჭრათ დამატებითი საფასური" + diff --git a/Tethering/res/values-mcc311-mnc480-kk/strings.xml b/Tethering/res/values-mcc311-mnc480-kk/strings.xml new file mode 100644 index 0000000000..1ddd6b419b --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-kk/strings.xml @@ -0,0 +1,24 @@ + + + + + "Тетеринг режимі интернет байланысынсыз пайдаланылуда" + "Құрылғыларды байланыстыру мүмкін емес" + "Тетерингіні өшіру" + "Хотспот немесе тетеринг қосулы" + "Роуминг кезінде қосымша ақы алынуы мүмкін." + diff --git a/Tethering/res/values-mcc311-mnc480-km/strings.xml b/Tethering/res/values-mcc311-mnc480-km/strings.xml new file mode 100644 index 0000000000..cf5a1379cc --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-km/strings.xml @@ -0,0 +1,24 @@ + + + + + "ការភ្ជាប់​មិនមានអ៊ីនធឺណិត​ទេ" + "មិនអាច​ភ្ជាប់ឧបករណ៍​បានទេ" + "បិទការភ្ជាប់" + "ហតស្ប៉ត ឬការភ្ជាប់​ត្រូវបានបើក" + "អាចមាន​ការគិតថ្លៃ​បន្ថែម នៅពេល​រ៉ូមីង" + diff --git a/Tethering/res/values-mcc311-mnc480-kn/strings.xml b/Tethering/res/values-mcc311-mnc480-kn/strings.xml new file mode 100644 index 0000000000..68ae68bc19 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-kn/strings.xml @@ -0,0 +1,24 @@ + + + + + "ಟೆಥರಿಂಗ್‌ ಯಾವುದೇ ಇಂಟರ್ನೆಟ್ ಕನೆಕ್ಷನ್ ಹೊಂದಿಲ್ಲ" + "ಸಾಧನಗಳನ್ನು ಕನೆಕ್ಟ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ" + "ಟೆಥರಿಂಗ್‌ ಆಫ್ ಮಾಡಿ" + "ಹಾಟ್‌ಸ್ಪಾಟ್ ಅಥವಾ ಟೆಥರಿಂಗ್‌ ಆನ್ ಆಗಿದೆ" + "ರೋಮಿಂಗ್‌ನಲ್ಲಿರುವಾಗ ಹೆಚ್ಚುವರಿ ಶುಲ್ಕಗಳು ಅನ್ವಯವಾಗಬಹುದು" + diff --git a/Tethering/res/values-mcc311-mnc480-ko/strings.xml b/Tethering/res/values-mcc311-mnc480-ko/strings.xml new file mode 100644 index 0000000000..17185ba2d0 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-ko/strings.xml @@ -0,0 +1,24 @@ + + + + + "테더링으로 인터넷을 사용할 수 없음" + "기기에서 연결할 수 없음" + "테더링 사용 중지" + "핫스팟 또는 테더링 켜짐" + "로밍 중에는 추가 요금이 발생할 수 있습니다." + diff --git a/Tethering/res/values-mcc311-mnc480-ky/strings.xml b/Tethering/res/values-mcc311-mnc480-ky/strings.xml new file mode 100644 index 0000000000..6a9fb9810c --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-ky/strings.xml @@ -0,0 +1,24 @@ + + + + + "Модем режими Интернети жок колдонулууда" + "Түзмөктөр туташпай жатат" + "Модем режимин өчүрүү" + "Байланыш түйүнү же модем режими күйүк" + "Роумингде кошумча акы алынышы мүмкүн" + diff --git a/Tethering/res/values-mcc311-mnc480-lo/strings.xml b/Tethering/res/values-mcc311-mnc480-lo/strings.xml new file mode 100644 index 0000000000..bcc4b57626 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-lo/strings.xml @@ -0,0 +1,24 @@ + + + + + "ການປ່ອຍສັນຍານບໍ່ມີອິນເຕີເນັດ" + "ອຸປະກອນບໍ່ສາມາດເຊື່ອມຕໍ່ໄດ້" + "ປິດການປ່ອຍສັນຍານ" + "ເປີດໃຊ້ຮັອດສະປອດ ຫຼື ການປ່ອຍສັນຍານຢູ່" + "ອາດມີຄ່າໃຊ້ຈ່າຍເພີ່ມເຕີມໃນລະຫວ່າງການໂຣມມິງ" + diff --git a/Tethering/res/values-mcc311-mnc480-lt/strings.xml b/Tethering/res/values-mcc311-mnc480-lt/strings.xml new file mode 100644 index 0000000000..011c2c11fb --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-lt/strings.xml @@ -0,0 +1,24 @@ + + + + + "Nėra įrenginio kaip modemo naudojimo interneto ryšio" + "Nepavyko susieti įrenginių" + "Išjungti įrenginio kaip modemo naudojimą" + "Įjungtas viešosios interneto prieigos taškas arba įrenginio kaip modemo naudojimas" + "Veikiant tarptinkliniam ryšiui gali būti taikomi papildomi mokesčiai" + diff --git a/Tethering/res/values-mcc311-mnc480-lv/strings.xml b/Tethering/res/values-mcc311-mnc480-lv/strings.xml new file mode 100644 index 0000000000..5cb2f3b7aa --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-lv/strings.xml @@ -0,0 +1,24 @@ + + + + + "Piesaistei nav interneta savienojuma" + "Nevar savienot ierīces" + "Izslēgt piesaisti" + "Ir ieslēgts tīklājs vai piesaiste" + "Viesabonēšanas laikā var tikt piemērota papildu samaksa" + diff --git a/Tethering/res/values-mcc311-mnc480-mk/strings.xml b/Tethering/res/values-mcc311-mnc480-mk/strings.xml new file mode 100644 index 0000000000..4cbfd887c5 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-mk/strings.xml @@ -0,0 +1,24 @@ + + + + + "Нема интернет преку мобилен" + "Уредите не може да се поврзат" + "Исклучи интернет преку мобилен" + "Точката на пристап или интернетот преку мобилен е вклучен" + "При роаминг може да се наплатат дополнителни трошоци" + diff --git a/Tethering/res/values-mcc311-mnc480-ml/strings.xml b/Tethering/res/values-mcc311-mnc480-ml/strings.xml new file mode 100644 index 0000000000..9cf4eaf34a --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-ml/strings.xml @@ -0,0 +1,24 @@ + + + + + "ടെതറിംഗിന് ഇന്റർനെറ്റ് ഇല്ല" + "ഉപകരണങ്ങൾ കണക്റ്റ് ചെയ്യാനാവില്ല" + "ടെതറിംഗ് ഓഫാക്കുക" + "ഹോട്ട്‌സ്‌പോട്ട് അല്ലെങ്കിൽ ടെതറിംഗ് ഓണാണ്" + "റോമിംഗ് ചെയ്യുമ്പോൾ അധിക നിരക്കുകൾ ബാധകമായേക്കാം" + diff --git a/Tethering/res/values-mcc311-mnc480-mn/strings.xml b/Tethering/res/values-mcc311-mnc480-mn/strings.xml new file mode 100644 index 0000000000..47c82c14d9 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-mn/strings.xml @@ -0,0 +1,24 @@ + + + + + "Модемд интернэт алга байна" + "Төхөөрөмжүүд холбогдох боломжгүй байна" + "Модем болгохыг унтраах" + "Сүлжээний цэг эсвэл модем болгох асаалттай байна" + "Роумингийн үеэр нэмэлт төлбөр нэхэмжилж болзошгүй" + diff --git a/Tethering/res/values-mcc311-mnc480-mr/strings.xml b/Tethering/res/values-mcc311-mnc480-mr/strings.xml new file mode 100644 index 0000000000..ad9e809ab2 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-mr/strings.xml @@ -0,0 +1,24 @@ + + + + + "टेदरिंगला इंटरनेट नाही" + "डिव्हाइस कनेक्ट होऊ शकत नाहीत" + "टेदरिंग बंद करा" + "हॉटस्पॉट किंवा टेदरिंग सुरू आहे" + "रोमिंगदरम्यान अतिरिक्त शुल्क लागू होऊ शकतात" + diff --git a/Tethering/res/values-mcc311-mnc480-ms/strings.xml b/Tethering/res/values-mcc311-mnc480-ms/strings.xml new file mode 100644 index 0000000000..e708cb8717 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-ms/strings.xml @@ -0,0 +1,24 @@ + + + + + "Penambatan tiada Internet" + "Peranti tidak dapat disambungkan" + "Matikan penambatan" + "Tempat liputan atau penambatan dihidupkan" + "Caj tambahan mungkin digunakan semasa perayauan" + diff --git a/Tethering/res/values-mcc311-mnc480-my/strings.xml b/Tethering/res/values-mcc311-mnc480-my/strings.xml new file mode 100644 index 0000000000..ba5462250b --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-my/strings.xml @@ -0,0 +1,24 @@ + + + + + "မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်းတွင် အင်တာနက် မရှိပါ" + "စက်များ ချိတ်ဆက်၍ မရပါ" + "မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်း ပိတ်ရန်" + "ဟော့စပေါ့ (သို့) မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်း ဖွင့်ထားသည်" + "ပြင်ပကွန်ရက်နှင့် ချိတ်ဆက်သည့်အခါ နောက်ထပ်ကျသင့်မှုများ ရှိနိုင်သည်" + diff --git a/Tethering/res/values-mcc311-mnc480-nb/strings.xml b/Tethering/res/values-mcc311-mnc480-nb/strings.xml new file mode 100644 index 0000000000..57db484a25 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-nb/strings.xml @@ -0,0 +1,24 @@ + + + + + "Internettdeling har ikke internettilgang" + "Enhetene kan ikke koble til" + "Slå av internettdeling" + "Wi-Fi-sone eller internettdeling er på" + "Ytterligere kostnader kan påløpe under roaming" + diff --git a/Tethering/res/values-mcc311-mnc480-ne/strings.xml b/Tethering/res/values-mcc311-mnc480-ne/strings.xml new file mode 100644 index 0000000000..1503244f50 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-ne/strings.xml @@ -0,0 +1,24 @@ + + + + + "टेदरिङमार्फत इन्टरनेट कनेक्सन प्राप्त हुन सकेन" + "यन्त्रहरू कनेक्ट गर्न सकिएन" + "टेदरिङ निष्क्रिय पार्नुहोस्" + "हटस्पट वा टेदरिङ सक्रिय छ" + "रोमिङ सेवा प्रयोग गर्दा अतिरिक्त शुल्क लाग्न सक्छ" + diff --git a/Tethering/res/values-mcc311-mnc480-nl/strings.xml b/Tethering/res/values-mcc311-mnc480-nl/strings.xml new file mode 100644 index 0000000000..b08133f4e5 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-nl/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tethering heeft geen internet" + "Apparaten kunnen niet worden verbonden" + "Tethering uitschakelen" + "Hotspot of tethering is ingeschakeld" + "Er kunnen extra kosten voor roaming in rekening worden gebracht." + diff --git a/Tethering/res/values-mcc311-mnc480-or/strings.xml b/Tethering/res/values-mcc311-mnc480-or/strings.xml new file mode 100644 index 0000000000..1ad4ca354a --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-or/strings.xml @@ -0,0 +1,24 @@ + + + + + "ଟିଥରିଂ ପାଇଁ କୌଣସି ଇଣ୍ଟର୍ନେଟ୍ ସଂଯୋଗ ନାହିଁ" + "ଡିଭାଇସଗୁଡ଼ିକ ସଂଯୋଗ କରାଯାଇପାରିବ ନାହିଁ" + "ଟିଥରିଂ ବନ୍ଦ କରନ୍ତୁ" + "ହଟସ୍ପଟ୍ କିମ୍ବା ଟିଥରିଂ ଚାଲୁ ଅଛି" + "ରୋମିଂରେ ଥିବା ସମୟରେ ଅତିରିକ୍ତ ଶୁଳ୍କ ଲାଗୁ ହୋଇପାରେ" + diff --git a/Tethering/res/values-mcc311-mnc480-pa/strings.xml b/Tethering/res/values-mcc311-mnc480-pa/strings.xml new file mode 100644 index 0000000000..88def563d8 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-pa/strings.xml @@ -0,0 +1,24 @@ + + + + + "ਟੈਦਰਿੰਗ ਕੋਲ ਇੰਟਰਨੈੱਟ ਪਹੁੰਚ ਨਹੀਂ ਹੈ" + "ਡੀਵਾਈਸ ਕਨੈਕਟ ਨਹੀਂ ਕੀਤੇ ਜਾ ਸਕਦੇ" + "ਟੈਦਰਿੰਗ ਬੰਦ ਕਰੋ" + "ਹੌਟਸਪੌਟ ਜਾਂ ਟੈਦਰਿੰਗ ਚਾਲੂ ਹੈ" + "ਰੋਮਿੰਗ ਦੌਰਾਨ ਵਧੀਕ ਖਰਚੇ ਲਾਗੂ ਹੋ ਸਕਦੇ ਹਨ" + diff --git a/Tethering/res/values-mcc311-mnc480-pl/strings.xml b/Tethering/res/values-mcc311-mnc480-pl/strings.xml new file mode 100644 index 0000000000..f9890abdc2 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-pl/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tethering nie ma internetu" + "Urządzenia nie mogą się połączyć" + "Wyłącz tethering" + "Hotspot lub tethering jest włączony" + "Podczas korzystania z roamingu mogą zostać naliczone dodatkowe opłaty" + diff --git a/Tethering/res/values-mcc311-mnc480-pt-rBR/strings.xml b/Tethering/res/values-mcc311-mnc480-pt-rBR/strings.xml new file mode 100644 index 0000000000..ce3b88479f --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-pt-rBR/strings.xml @@ -0,0 +1,24 @@ + + + + + "O tethering não tem Internet" + "Não é possível conectar os dispositivos" + "Desativar o tethering" + "Ponto de acesso ou tethering ativado" + "Pode haver cobranças extras durante o roaming" + diff --git a/Tethering/res/values-mcc311-mnc480-pt-rPT/strings.xml b/Tethering/res/values-mcc311-mnc480-pt-rPT/strings.xml new file mode 100644 index 0000000000..7e883ea576 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-pt-rPT/strings.xml @@ -0,0 +1,24 @@ + + + + + "A ligação (à Internet) via telemóvel não tem Internet" + "Não é possível ligar os dispositivos" + "Desativar ligação (à Internet) via telemóvel" + "A zona Wi-Fi ou a ligação (à Internet) via telemóvel está ativada" + "Podem aplicar-se custos adicionais em roaming." + diff --git a/Tethering/res/values-mcc311-mnc480-pt/strings.xml b/Tethering/res/values-mcc311-mnc480-pt/strings.xml new file mode 100644 index 0000000000..ce3b88479f --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-pt/strings.xml @@ -0,0 +1,24 @@ + + + + + "O tethering não tem Internet" + "Não é possível conectar os dispositivos" + "Desativar o tethering" + "Ponto de acesso ou tethering ativado" + "Pode haver cobranças extras durante o roaming" + diff --git a/Tethering/res/values-mcc311-mnc480-ro/strings.xml b/Tethering/res/values-mcc311-mnc480-ro/strings.xml new file mode 100644 index 0000000000..1009417316 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-ro/strings.xml @@ -0,0 +1,24 @@ + + + + + "Procesul de tethering nu are internet" + "Dispozitivele nu se pot conecta" + "Dezactivați procesul de tethering" + "S-a activat hotspotul sau tethering" + "Se pot aplica taxe suplimentare pentru roaming" + diff --git a/Tethering/res/values-mcc311-mnc480-ru/strings.xml b/Tethering/res/values-mcc311-mnc480-ru/strings.xml new file mode 100644 index 0000000000..88683bed95 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-ru/strings.xml @@ -0,0 +1,24 @@ + + + + + "Режим модема используется без доступа к Интернету" + "Невозможно подключить устройства." + "Отключить режим модема" + "Включены точка доступа или режим модема" + "За использование услуг связи в роуминге может взиматься дополнительная плата." + diff --git a/Tethering/res/values-mcc311-mnc480-si/strings.xml b/Tethering/res/values-mcc311-mnc480-si/strings.xml new file mode 100644 index 0000000000..176bcdb797 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-si/strings.xml @@ -0,0 +1,24 @@ + + + + + "ටෙදරින් හට අන්තර්ජාලය නැත" + "උපාංගවලට සම්බන්ධ විය නොහැකිය" + "ටෙදරින් ක්‍රියාවිරහිත කරන්න" + "හොට්ස්පොට් හෝ ටෙදරින් ක්‍රියාත්මකයි" + "රෝමිං අතරතුර අමතර ගාස්තු අදාළ විය හැකිය" + diff --git a/Tethering/res/values-mcc311-mnc480-sk/strings.xml b/Tethering/res/values-mcc311-mnc480-sk/strings.xml new file mode 100644 index 0000000000..b9e2127fa8 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-sk/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tethering nemá internetové pripojenie" + "Zariadenia sa nemôžu pripojiť" + "Vypnúť tethering" + "Je zapnutý hotspot alebo tethering" + "Počas roamingu vám môžu byť účtované ďalšie poplatky" + diff --git a/Tethering/res/values-mcc311-mnc480-sl/strings.xml b/Tethering/res/values-mcc311-mnc480-sl/strings.xml new file mode 100644 index 0000000000..e8140e686a --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-sl/strings.xml @@ -0,0 +1,24 @@ + + + + + "Internetna povezava prek mobilnega telefona ni vzpostavljena" + "Napravi se ne moreta povezati" + "Izklopi internetno povezavo prek mobilnega telefona" + "Dostopna točka ali internetna povezava prek mobilnega telefona je vklopljena" + "Med gostovanjem lahko nastanejo dodatni stroški" + diff --git a/Tethering/res/values-mcc311-mnc480-sq/strings.xml b/Tethering/res/values-mcc311-mnc480-sq/strings.xml new file mode 100644 index 0000000000..61e698d6e8 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-sq/strings.xml @@ -0,0 +1,24 @@ + + + + + "Ndarja e internetit nuk ka internet" + "Pajisjet nuk mund të lidhen" + "Çaktivizo ndarjen e internetit" + "Zona e qasjes për internet ose ndarja e internetit është aktive" + "Mund të zbatohen tarifime shtesë kur je në roaming" + diff --git a/Tethering/res/values-mcc311-mnc480-sr/strings.xml b/Tethering/res/values-mcc311-mnc480-sr/strings.xml new file mode 100644 index 0000000000..b4c411c354 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-sr/strings.xml @@ -0,0 +1,24 @@ + + + + + "Привезивање нема приступ интернету" + "Повезивање уређаја није успело" + "Искључи привезивање" + "Укључен је хотспот или привезивање" + "Можда важе додатни трошкови у ромингу" + diff --git a/Tethering/res/values-mcc311-mnc480-sv/strings.xml b/Tethering/res/values-mcc311-mnc480-sv/strings.xml new file mode 100644 index 0000000000..4f543e47b9 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-sv/strings.xml @@ -0,0 +1,24 @@ + + + + + "Det finns ingen internetanslutning för internetdelningen" + "Enheterna kan inte anslutas" + "Inaktivera internetdelning" + "Surfzon eller internetdelning har aktiverats" + "Ytterligare avgifter kan tillkomma vid roaming" + diff --git a/Tethering/res/values-mcc311-mnc480-sw/strings.xml b/Tethering/res/values-mcc311-mnc480-sw/strings.xml new file mode 100644 index 0000000000..ac347ab485 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-sw/strings.xml @@ -0,0 +1,24 @@ + + + + + "Kipengele cha kusambaza mtandao hakina intaneti" + "Imeshindwa kuunganisha vifaa" + "Zima kipengele cha kusambaza mtandao" + "Umewasha kipengele cha kusambaza mtandao au mtandao pepe" + "Huenda ukatozwa gharama za ziada ukitumia mitandao ya ng\'ambo" + diff --git a/Tethering/res/values-mcc311-mnc480-ta/strings.xml b/Tethering/res/values-mcc311-mnc480-ta/strings.xml new file mode 100644 index 0000000000..2ea2467e58 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-ta/strings.xml @@ -0,0 +1,24 @@ + + + + + "இணைப்பு முறைக்கு இணைய இணைப்பு இல்லை" + "சாதனங்களால் இணைய முடியவில்லை" + "இணைப்பு முறையை ஆஃப் செய்" + "ஹாட்ஸ்பாட் அல்லது இணைப்பு முறை ஆன் செய்யப்பட்டுள்ளது" + "ரோமிங்கின்போது கூடுதல் கட்டணங்கள் விதிக்கப்படக்கூடும்" + diff --git a/Tethering/res/values-mcc311-mnc480-te/strings.xml b/Tethering/res/values-mcc311-mnc480-te/strings.xml new file mode 100644 index 0000000000..9360297dd8 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-te/strings.xml @@ -0,0 +1,24 @@ + + + + + "టెథరింగ్ చేయడానికి ఇంటర్నెట్ కనెక్షన్ లేదు" + "పరికరాలు కనెక్ట్ అవ్వడం లేదు" + "టెథరింగ్‌ను ఆఫ్ చేయండి" + "హాట్‌స్పాట్ లేదా టెథరింగ్ ఆన్‌లో ఉంది" + "రోమింగ్‌లో ఉన్నప్పుడు అదనపు ఛార్జీలు వర్తించవచ్చు" + diff --git a/Tethering/res/values-mcc311-mnc480-th/strings.xml b/Tethering/res/values-mcc311-mnc480-th/strings.xml new file mode 100644 index 0000000000..9c4d7e08f2 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-th/strings.xml @@ -0,0 +1,24 @@ + + + + + "การเชื่อมต่ออินเทอร์เน็ตผ่านมือถือไม่มีอินเทอร์เน็ต" + "อุปกรณ์เชื่อมต่อไม่ได้" + "ปิดการเชื่อมต่ออินเทอร์เน็ตผ่านมือถือ" + "ฮอตสปอตหรือการเชื่อมต่ออินเทอร์เน็ตผ่านมือถือเปิดอยู่" + "อาจมีค่าใช้จ่ายเพิ่มเติมขณะโรมมิ่ง" + diff --git a/Tethering/res/values-mcc311-mnc480-tl/strings.xml b/Tethering/res/values-mcc311-mnc480-tl/strings.xml new file mode 100644 index 0000000000..a7c78a5932 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-tl/strings.xml @@ -0,0 +1,24 @@ + + + + + "Walang internet ang pag-tether" + "Hindi makakonekta ang mga device" + "I-off ang pag-tether" + "Naka-on ang Hotspot o pag-tether" + "Posibleng magkaroon ng mga karagdagang singil habang nagro-roam" + diff --git a/Tethering/res/values-mcc311-mnc480-tr/strings.xml b/Tethering/res/values-mcc311-mnc480-tr/strings.xml new file mode 100644 index 0000000000..93da2c3f79 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-tr/strings.xml @@ -0,0 +1,24 @@ + + + + + "Tethering\'in internet bağlantısı yok" + "Cihazlar bağlanamıyor" + "Tethering\'i kapat" + "Hotspot veya tethering açık" + "Dolaşım sırasında ek ücretler uygulanabilir" + diff --git a/Tethering/res/values-mcc311-mnc480-uk/strings.xml b/Tethering/res/values-mcc311-mnc480-uk/strings.xml new file mode 100644 index 0000000000..ee0dcd2c4b --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-uk/strings.xml @@ -0,0 +1,24 @@ + + + + + "Телефон, який використовується як модем, не підключений до Інтернету" + "Не вдається підключити пристрої" + "Вимкнути використання телефона як модема" + "Увімкнено точку доступу або використання телефона як модема" + "У роумінгу може стягуватися додаткова плата" + diff --git a/Tethering/res/values-mcc311-mnc480-ur/strings.xml b/Tethering/res/values-mcc311-mnc480-ur/strings.xml new file mode 100644 index 0000000000..41cd28eef9 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-ur/strings.xml @@ -0,0 +1,24 @@ + + + + + "ٹیدرنگ میں انٹرنیٹ نہیں ہے" + "آلات منسلک نہیں ہو سکتے" + "ٹیدرنگ آف کریں" + "ہاٹ اسپاٹ یا ٹیدرنگ آن ہے" + "رومنگ کے دوران اضافی چارجز لاگو ہو سکتے ہیں" + diff --git a/Tethering/res/values-mcc311-mnc480-uz/strings.xml b/Tethering/res/values-mcc311-mnc480-uz/strings.xml new file mode 100644 index 0000000000..c847bc943b --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-uz/strings.xml @@ -0,0 +1,24 @@ + + + + + "Modem internetga ulanmagan" + "Qurilmalar ulanmadi" + "Modem rejimini faolsizlantirish" + "Hotspot yoki modem rejimi yoniq" + "Rouming vaqtida qoʻshimcha haq olinishi mumkin" + diff --git a/Tethering/res/values-mcc311-mnc480-vi/strings.xml b/Tethering/res/values-mcc311-mnc480-vi/strings.xml new file mode 100644 index 0000000000..a74326f09e --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-vi/strings.xml @@ -0,0 +1,24 @@ + + + + + "Không có Internet để chia sẻ kết Internet" + "Các thiết bị không thể kết nối" + "Tắt tính năng chia sẻ Internet" + "Điểm phát sóng hoặc tính năng chia sẻ Internet đang bật" + "Bạn có thể mất thêm phí dữ liệu khi chuyển vùng" + diff --git a/Tethering/res/values-mcc311-mnc480-zh-rCN/strings.xml b/Tethering/res/values-mcc311-mnc480-zh-rCN/strings.xml new file mode 100644 index 0000000000..d7370036e3 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-zh-rCN/strings.xml @@ -0,0 +1,24 @@ + + + + + "共享网络未连接到互联网" + "设备无法连接" + "关闭网络共享" + "热点或网络共享已开启" + "漫游时可能会产生额外的费用" + diff --git a/Tethering/res/values-mcc311-mnc480-zh-rHK/strings.xml b/Tethering/res/values-mcc311-mnc480-zh-rHK/strings.xml new file mode 100644 index 0000000000..f378a9dc2c --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-zh-rHK/strings.xml @@ -0,0 +1,24 @@ + + + + + "無法透過網絡共享連線至互聯網" + "裝置無法連接" + "關閉網絡共享" + "熱點或網絡共享已開啟" + "漫遊時可能需要支付額外費用" + diff --git a/Tethering/res/values-mcc311-mnc480-zh-rTW/strings.xml b/Tethering/res/values-mcc311-mnc480-zh-rTW/strings.xml new file mode 100644 index 0000000000..cd653df1da --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-zh-rTW/strings.xml @@ -0,0 +1,24 @@ + + + + + "無法透過網路共用連上網際網路" + "裝置無法連線" + "關閉網路共用" + "無線基地台或網路共用已開啟" + "使用漫遊服務可能須支付額外費用" + diff --git a/Tethering/res/values-mcc311-mnc480-zu/strings.xml b/Tethering/res/values-mcc311-mnc480-zu/strings.xml new file mode 100644 index 0000000000..32f6df56f1 --- /dev/null +++ b/Tethering/res/values-mcc311-mnc480-zu/strings.xml @@ -0,0 +1,24 @@ + + + + + "Ukusebenzisa ifoni njengemodemu akunayo i-inthanethi" + "Amadivayisi awakwazi ukuxhumeka" + "Vala ukusebenzisa ifoni njengemodemu" + "I-hotspot noma ukusebenzisa ifoni njengemodemu kuvuliwe" + "Kungaba nezinkokhelo ezengeziwe uma uzula" + diff --git a/Tethering/res/values-mk/strings.xml b/Tethering/res/values-mk/strings.xml index 0fab8aa476..9ad9b9a589 100644 --- a/Tethering/res/values-mk/strings.xml +++ b/Tethering/res/values-mk/strings.xml @@ -1,8 +1,29 @@ + + - "Поврзувањето или точката на пристап се активни" - "Допрете за поставување." - "Врзувањето е оневозможено" - "Контактирајте со администраторот за детали" + "Активно е врзување или точка на пристап" + "Допрете за поставување." + "Врзувањето е оневозможено" + "Контактирајте со администраторот за детали" + "Статус на точката на пристап и врзувањето" + + + + + diff --git a/Tethering/res/values-ml/strings.xml b/Tethering/res/values-ml/strings.xml index fd7e556e38..9db79ce220 100644 --- a/Tethering/res/values-ml/strings.xml +++ b/Tethering/res/values-ml/strings.xml @@ -1,8 +1,29 @@ + + - "ടെതറിംഗ് അല്ലെങ്കിൽ ഹോട്ട്സ്‌പോട്ട് സജീവമാണ്" - "സജ്ജമാക്കാൻ ടാപ്പുചെയ്യുക." - "ടെതറിംഗ് പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു" - "വിശദവിവരങ്ങൾക്ക് നിങ്ങളുടെ അഡ്മിനെ ബന്ധപ്പെടുക" + "ടെതറിംഗ് അല്ലെങ്കിൽ ഹോട്ട്സ്‌പോട്ട് സജീവമാണ്" + "സജ്ജീകരിക്കാൻ ടാപ്പ് ചെയ്യുക." + "ടെതറിംഗ് പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു" + "വിശദാംശങ്ങൾക്ക് നിങ്ങളുടെ അഡ്മിനെ ബന്ധപ്പെടുക" + "ഹോട്ട്‌സ്പോട്ടിന്റെയും ടെതറിംഗിന്റെയും നില" + + + + + diff --git a/Tethering/res/values-mn/strings.xml b/Tethering/res/values-mn/strings.xml index 4596577c5d..42d1edbace 100644 --- a/Tethering/res/values-mn/strings.xml +++ b/Tethering/res/values-mn/strings.xml @@ -1,8 +1,29 @@ + + - "Модем болгох эсвэл идэвхтэй цэг болгох" - "Тохируулахын тулд товшино уу." - "Модем болгох боломжгүй байна" - "Дэлгэрэнгүй мэдээлэл авахын тулд админтайгаа холбогдоно уу" + "Модем болгох эсвэл сүлжээний цэг идэвхтэй байна" + "Тохируулахын тулд товшино уу." + "Модем болгохыг идэвхгүй болгосон" + "Дэлгэрэнгүй мэдээлэл авахын тулд админтайгаа холбогдоно уу" + "Сүлжээний цэг болон модем болгох төлөв" + + + + + diff --git a/Tethering/res/values-mr/strings.xml b/Tethering/res/values-mr/strings.xml index 85c9ade4fe..13995b6b8a 100644 --- a/Tethering/res/values-mr/strings.xml +++ b/Tethering/res/values-mr/strings.xml @@ -1,8 +1,29 @@ + + - "टेदरिंग किंवा हॉटस्पॉट सक्रिय" - "सेट करण्यासाठी टॅप करा." - "टेदरिंग बंद आहे" - "तपशीलांसाठी तुमच्या प्रशासकाशी संपर्क साधा" + "टेदरिंग किंवा हॉटस्पॉट अ‍ॅक्टिव्ह आहे" + "सेट करण्यासाठी टॅप करा." + "टेदरिंग बंद केले आहे" + "तपशीलांसाठी तुमच्या ॲडमिनशी संपर्क साधा" + "हॉटस्पॉट आणि टेदरिंगची स्थिती" + + + + + diff --git a/Tethering/res/values-ms/strings.xml b/Tethering/res/values-ms/strings.xml index ec6bdbda08..d6a67f37b1 100644 --- a/Tethering/res/values-ms/strings.xml +++ b/Tethering/res/values-ms/strings.xml @@ -1,8 +1,29 @@ + + - "Penambatan atau titik panas aktif" - "Ketik untuk membuat persediaan." - "Penambatan dilumpuhkan" - "Hubungi pentadbir anda untuk maklumat lanjut" + "Penambatan atau tempat liputan aktif" + "Ketik untuk membuat persediaan." + "Penambatan dilumpuhkan" + "Hubungi pentadbir anda untuk mendapatkan maklumat lanjut" + "Status tempat liputan & penambatan" + + + + + diff --git a/Tethering/res/values-my/strings.xml b/Tethering/res/values-my/strings.xml index 83978b67d4..49f6b88a75 100644 --- a/Tethering/res/values-my/strings.xml +++ b/Tethering/res/values-my/strings.xml @@ -1,8 +1,29 @@ + + - "တဆင့်ပြန်လည်လွှင့်ခြင်း သို့မဟုတ် ဟော့စပေါ့ ဖွင့်ထားသည်" - "စနစ်ထည့်သွင်းရန် တို့ပါ။" - "မိုဘိုင်းဖုန်းကို မိုဒမ်အဖြစ်သုံးခြင်းအား ပိတ်ထားသည်" - "အသေးစိတ်အချက်အလက်များအတွက် သင့်စီမံခန့်ခွဲသူကို ဆက်သွယ်ပါ" + "မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်း သို့မဟုတ် ဟော့စပေါ့ ဖွင့်ထားသည်" + "စနစ်ထည့်သွင်းရန် တို့ပါ။" + "မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်းကို ပိတ်ထားသည်" + "အသေးစိတ်အတွက် သင့်စီမံခန့်ခွဲသူကို ဆက်သွယ်ပါ" + "ဟော့စပေါ့နှင့် မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်း အခြေအနေ" + + + + + diff --git a/Tethering/res/values-nb/strings.xml b/Tethering/res/values-nb/strings.xml index 9abf32dd7b..9594e0a70a 100644 --- a/Tethering/res/values-nb/strings.xml +++ b/Tethering/res/values-nb/strings.xml @@ -1,8 +1,29 @@ + + - "Internettdeling eller trådløs sone er aktiv" - "Trykk for å konfigurere." - "Internettdeling er slått av" - "Ta kontakt med administratoren din for å få mer informasjon" + "Internettdeling eller Wi-Fi-sone er aktiv" + "Trykk for å konfigurere." + "Internettdeling er slått av" + "Ta kontakt med administratoren din for å få mer informasjon" + "Status for Wi-Fi-sone og internettdeling" + + + + + diff --git a/Tethering/res/values-ne/strings.xml b/Tethering/res/values-ne/strings.xml index c8869298a5..72ae3a80a9 100644 --- a/Tethering/res/values-ne/strings.xml +++ b/Tethering/res/values-ne/strings.xml @@ -1,8 +1,29 @@ + + - "टेथर गर्ने वा हटस्पट सक्रिय" - "सेटअप गर्न ट्याप गर्नुहोस्।" - "टेदरिङलाई असक्षम पारिएको छ" - "विवरणहरूका लागि आफ्ना प्रशासकलाई सम्पर्क गर्नुहोस्" + "टेदरिङ वा हटस्पट सक्रिय छ" + "सेटअप गर्न ट्याप गर्नुहोस्।" + "टेदरिङ सुविधा असक्षम पारिएको छ" + "विवरणहरूका लागि आफ्ना प्रशासकलाई सम्पर्क गर्नुहोस्" + "हटस्पट तथा टेदरिङको स्थिति" + + + + + diff --git a/Tethering/res/values-nl/strings.xml b/Tethering/res/values-nl/strings.xml index 0ec4bff621..18b2bbfc76 100644 --- a/Tethering/res/values-nl/strings.xml +++ b/Tethering/res/values-nl/strings.xml @@ -1,8 +1,29 @@ + + - "Tethering of hotspot actief" - "Tik om in te stellen." - "Tethering is uitgeschakeld" - "Neem contact op met je beheerder voor meer informatie" + "Tethering of hotspot actief" + "Tik om in te stellen." + "Tethering is uitgeschakeld" + "Neem contact op met je beheerder voor meer informatie" + "Status van hotspot en tethering" + + + + + diff --git a/Tethering/res/values-or/strings.xml b/Tethering/res/values-or/strings.xml index 457685795a..a15a6db42a 100644 --- a/Tethering/res/values-or/strings.xml +++ b/Tethering/res/values-or/strings.xml @@ -1,8 +1,29 @@ + + - "ଟିଥରିଙ୍ଗ କିମ୍ୱା ହଟସ୍ପଟ୍‌ ସକ୍ରିୟ ଅଛି" - "ସେଟଅପ୍‍ କରିବାକୁ ଟାପ୍‍ କରନ୍ତୁ।" - "ଟିଥରିଙ୍ଗ ଅକ୍ଷମ କରାଯାଇଛି" - "ବିବରଣୀ ପାଇଁ ନିଜ ଆଡମିନ୍‌ଙ୍କ ସହ ଯୋଗାଯୋଗ କରନ୍ତୁ" + "ଟିଥେରିଂ କିମ୍ୱା ହଟସ୍ପଟ୍ ସକ୍ରିୟ ଅଛି" + "ସେଟ୍ ଅପ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ।" + "ଟିଥେରିଂ ଅକ୍ଷମ କରାଯାଇଛି" + "ବିବରଣୀଗୁଡ଼ିକ ପାଇଁ ଆପଣଙ୍କ ଆଡମିନଙ୍କ ସହ ଯୋଗାଯୋଗ କରନ୍ତୁ" + "ହଟସ୍ପଟ୍ ଓ ଟିଥେରିଂ ସ୍ଥିତି" + + + + + diff --git a/Tethering/res/values-pa/strings.xml b/Tethering/res/values-pa/strings.xml index deddf2ea27..a8235e423e 100644 --- a/Tethering/res/values-pa/strings.xml +++ b/Tethering/res/values-pa/strings.xml @@ -1,8 +1,29 @@ + + - "ਟੈਦਰਿੰਗ ਜਾਂ ਹੌਟਸਪੌਟ ਕਿਰਿਆਸ਼ੀਲ" - "ਸਥਾਪਤ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।" - "ਟੈਦਰਿੰਗ ਨੂੰ ਅਯੋਗ ਬਣਾਇਆ ਗਿਆ ਹੈ" - "ਵੇਰਵਿਆਂ ਲਈ ਆਪਣੇ ਪ੍ਰਸ਼ਾਸਕ ਨੂੰ ਸੰਪਰਕ ਕਰੋ" + "ਟੈਦਰਿੰਗ ਜਾਂ ਹੌਟਸਪੌਟ ਕਿਰਿਆਸ਼ੀਲ" + "ਸੈੱਟਅੱਪ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।" + "ਟੈਦਰਿੰਗ ਨੂੰ ਬੰਦ ਕੀਤਾ ਗਿਆ ਹੈ" + "ਵੇਰਵਿਆਂ ਲਈ ਆਪਣੇ ਪ੍ਰਸ਼ਾਸਕ ਨਾਲ ਸੰਪਰਕ ਕਰੋ" + "ਹੌਟਸਪੌਟ ਅਤੇ ਟੈਦਰਿੰਗ ਦੀ ਸਥਿਤੀ" + + + + + diff --git a/Tethering/res/values-pl/strings.xml b/Tethering/res/values-pl/strings.xml index 48d8468935..ccb017d43f 100644 --- a/Tethering/res/values-pl/strings.xml +++ b/Tethering/res/values-pl/strings.xml @@ -1,8 +1,29 @@ + + - "Aktywny tethering lub punkt dostępu" - "Kliknij, by skonfigurować." - "Tethering został wyłączony" - "Aby uzyskać szczegółowe informacje, skontaktuj się z administratorem" + "Aktywny tethering lub punkt dostępu" + "Kliknij, by skonfigurować" + "Tethering został wyłączony" + "Aby uzyskać szczegółowe informacje, skontaktuj się z administratorem" + "Hotspot i tethering – stan" + + + + + diff --git a/Tethering/res/values-pt-rBR/strings.xml b/Tethering/res/values-pt-rBR/strings.xml index 32c22b8713..a0a4745f93 100644 --- a/Tethering/res/values-pt-rBR/strings.xml +++ b/Tethering/res/values-pt-rBR/strings.xml @@ -1,8 +1,29 @@ + + - "Ponto de acesso ou tethering ativo" - "Toque para configurar." - "Tethering desativado" - "Fale com seu administrador para saber detalhes" + "Ponto de acesso ou tethering ativo" + "Toque para configurar." + "Tethering desativado" + "Fale com seu administrador para saber detalhes" + "Status de ponto de acesso e tethering" + + + + + diff --git a/Tethering/res/values-pt-rPT/strings.xml b/Tethering/res/values-pt-rPT/strings.xml index 641e22f44f..e3f03fcc69 100644 --- a/Tethering/res/values-pt-rPT/strings.xml +++ b/Tethering/res/values-pt-rPT/strings.xml @@ -1,8 +1,29 @@ + + - "Ligação ponto a ponto ou hotspot activos" - "Toque para configurar." - "A ligação (à Internet) via telemóvel está desativada." - "Contacte o gestor para obter detalhes." + "Ligação (à Internet) via telemóvel ou zona Wi-Fi ativas" + "Toque para configurar." + "A ligação (à Internet) via telemóvel está desativada." + "Contacte o administrador para obter detalhes." + "Estado da zona Wi-Fi e da ligação (à Internet) via telemóvel" + + + + + diff --git a/Tethering/res/values-pt/strings.xml b/Tethering/res/values-pt/strings.xml index 32c22b8713..a0a4745f93 100644 --- a/Tethering/res/values-pt/strings.xml +++ b/Tethering/res/values-pt/strings.xml @@ -1,8 +1,29 @@ + + - "Ponto de acesso ou tethering ativo" - "Toque para configurar." - "Tethering desativado" - "Fale com seu administrador para saber detalhes" + "Ponto de acesso ou tethering ativo" + "Toque para configurar." + "Tethering desativado" + "Fale com seu administrador para saber detalhes" + "Status de ponto de acesso e tethering" + + + + + diff --git a/Tethering/res/values-ro/strings.xml b/Tethering/res/values-ro/strings.xml index f861f733b4..5706a4a69c 100644 --- a/Tethering/res/values-ro/strings.xml +++ b/Tethering/res/values-ro/strings.xml @@ -1,8 +1,29 @@ + + - "Tethering sau hotspot activ" - "Atingeți ca să configurați." - "Tetheringul este dezactivat" - "Contactați administratorul pentru detalii" + "Tethering sau hotspot activ" + "Atingeți ca să configurați." + "Tetheringul este dezactivat" + "Contactați administratorul pentru detalii" + "Starea hotspotului și a tetheringului" + + + + + diff --git a/Tethering/res/values-ru/strings.xml b/Tethering/res/values-ru/strings.xml index 027cb410c5..7cb6f7db3f 100644 --- a/Tethering/res/values-ru/strings.xml +++ b/Tethering/res/values-ru/strings.xml @@ -1,8 +1,29 @@ + + - "Включен режим модема" - "Нажмите, чтобы настроить." - "Включить режим модема нельзя" - "Обратитесь к администратору, чтобы узнать подробности." + "Включен режим модема или точка доступа" + "Нажмите, чтобы настроить." + "Использование телефона в качестве модема запрещено" + "Чтобы узнать подробности, обратитесь к администратору." + "Статус хот-спота и режима модема" + + + + + diff --git a/Tethering/res/values-si/strings.xml b/Tethering/res/values-si/strings.xml index 7d8599f2c2..ec34c22de7 100644 --- a/Tethering/res/values-si/strings.xml +++ b/Tethering/res/values-si/strings.xml @@ -1,8 +1,29 @@ + + - "ටෙදරින් හෝ හොට්ස්පොට් සක්‍රීයයි" - "පිහිටුවීමට තට්ටු කරන්න." - "ටෙදරින් අබල කර ඇත" - "විස්තර සඳහා ඔබගේ පරිපාලක අමතන්න" + "ටෙදරින් හෝ හොට්ස්පොට් සක්‍රීයයි" + "පිහිටුවීමට තට්ටු කරන්න." + "ටෙදරින් අබල කර ඇත" + "විස්තර සඳහා ඔබගේ පරිපාලක අමතන්න" + "හොට්ස්පොට් & ටෙදරින් තත්ත්වය" + + + + + diff --git a/Tethering/res/values-sk/strings.xml b/Tethering/res/values-sk/strings.xml index a8fe297c00..43e787c84f 100644 --- a/Tethering/res/values-sk/strings.xml +++ b/Tethering/res/values-sk/strings.xml @@ -1,8 +1,29 @@ + + - "Tethering alebo prístupový bod je aktívny" - "Klepnutím prejdete na nastavenie." - "Tethering je deaktivovaný" - "O podrobnosti požiadajte svojho správcu" + "Tethering alebo prístupový bod je aktívny" + "Klepnutím prejdete na nastavenie." + "Tethering je deaktivovaný" + "O podrobnosti požiadajte svojho správcu" + "Stav hotspotu a tetheringu" + + + + + diff --git a/Tethering/res/values-sl/strings.xml b/Tethering/res/values-sl/strings.xml index b5e5e3856f..59433626a1 100644 --- a/Tethering/res/values-sl/strings.xml +++ b/Tethering/res/values-sl/strings.xml @@ -1,8 +1,29 @@ + + - "Aktivna povezava z internetom ali dostopna točka sta aktivni" - "Dotaknite se, če želite nastaviti." - "Povezava z internetom prek mobilnega telefona je onemogočena" - "Za podrobnosti se obrnite na skrbnika" + "Povezava z internetom prek mobilnega telefona ali dostopna točka je aktivna" + "Dotaknite se, če želite nastaviti." + "Povezava z internetom prek mobilnega telefona je onemogočena" + "Za podrobnosti se obrnite na skrbnika" + "Stanje dostopne točke in povezave z internetom prek mobilnega telefona" + + + + + diff --git a/Tethering/res/values-sq/strings.xml b/Tethering/res/values-sq/strings.xml index fdd4906cc5..21e11558bb 100644 --- a/Tethering/res/values-sq/strings.xml +++ b/Tethering/res/values-sq/strings.xml @@ -1,8 +1,29 @@ + + - "Lidhja e çiftimit ose ajo e qasjes në zona publike interneti është aktive" - "Trokit për ta konfiguruar." - "Lidhja e çiftimit është çaktivizuar" - "Kontakto me administratorin për detaje" + "Ndarja e internetit ose zona e qasjes së internetit është aktive" + "Trokit për ta konfiguruar." + "Ndarja e internetit është çaktivizuar" + "Kontakto me administratorin për detaje" + "Statusi i zonës së qasjes dhe ndarjes së internetit" + + + + + diff --git a/Tethering/res/values-sr/strings.xml b/Tethering/res/values-sr/strings.xml index 9fab345897..e2e4dc6361 100644 --- a/Tethering/res/values-sr/strings.xml +++ b/Tethering/res/values-sr/strings.xml @@ -1,8 +1,29 @@ + + - "Активно повезивање са интернетом преко мобилног уређаја или хотспот" - "Додирните да бисте подесили." - "Привезивање је онемогућено" - "Потражите детаље од администратора" + "Привезивање или хотспот је активан" + "Додирните да бисте подесили." + "Привезивање је онемогућено" + "Потражите детаље од администратора" + "Статус хотспота и привезивања" + + + + + diff --git a/Tethering/res/values-sv/strings.xml b/Tethering/res/values-sv/strings.xml index 10eeb0fe12..72702c2858 100644 --- a/Tethering/res/values-sv/strings.xml +++ b/Tethering/res/values-sv/strings.xml @@ -1,8 +1,29 @@ + + - "Internetdelning eller surfzon aktiverad" - "Tryck om du vill konfigurera." - "Internetdelning har inaktiverats" - "Kontakta administratören om du vill veta mer" + "Internetdelning eller surfzon har aktiverats" + "Tryck om du vill konfigurera." + "Internetdelning har inaktiverats" + "Kontakta administratören om du vill veta mer" + "Trådlös surfzon och internetdelning har inaktiverats" + + + + + diff --git a/Tethering/res/values-sw/strings.xml b/Tethering/res/values-sw/strings.xml index 3353963077..65e4aa8ceb 100644 --- a/Tethering/res/values-sw/strings.xml +++ b/Tethering/res/values-sw/strings.xml @@ -1,8 +1,29 @@ + + - "Kushiriki au kusambaza intaneti kumewashwa" - "Gusa ili uweke mipangilio." - "Umezima kipengele cha kusambaza mtandao" - "Wasiliana na msimamizi wako ili upate maelezo zaidi" + "Kusambaza mtandao au mtandaopepe umewashwa" + "Gusa ili uweke mipangilio." + "Umezima kipengele cha kusambaza mtandao" + "Wasiliana na msimamizi wako ili upate maelezo zaidi" + "Mtandaopepe na hali ya kusambaza mtandao" + + + + + diff --git a/Tethering/res/values-ta/strings.xml b/Tethering/res/values-ta/strings.xml index b1e5cc2413..4aba62d4ab 100644 --- a/Tethering/res/values-ta/strings.xml +++ b/Tethering/res/values-ta/strings.xml @@ -1,8 +1,29 @@ + + - "டெதெரிங்/ஹாட்ஸ்பாட் இயங்குகிறது" - "அமைக்க, தட்டவும்." - "இணைப்பு முறை முடக்கப்பட்டுள்ளது" - "விவரங்களுக்கு, உங்கள் நிர்வாகியைத் தொடர்புகொள்ளவும்" + "டெதெரிங் அல்லது ஹாட்ஸ்பாட் இயங்குகிறது" + "அமைக்க, தட்டவும்." + "டெதெரிங் முடக்கப்பட்டுள்ளது" + "விவரங்களுக்கு உங்கள் நிர்வாகியைத் தொடர்புகொள்ளவும்" + "ஹாட்ஸ்பாட் & டெதெரிங் நிலை" + + + + + diff --git a/Tethering/res/values-te/strings.xml b/Tethering/res/values-te/strings.xml index aae40dee40..1f91791341 100644 --- a/Tethering/res/values-te/strings.xml +++ b/Tethering/res/values-te/strings.xml @@ -1,8 +1,29 @@ + + - "టీథర్ చేయబడినది లేదా హాట్‌స్పాట్ సక్రియంగా ఉండేది" - "సెటప్ చేయడానికి నొక్కండి." - "టెథెరింగ్ నిలిపివేయబడింది" - "వివరాల కోసం మీ నిర్వాహకులను సంప్రదించండి" + "టెథరింగ్ లేదా హాట్‌స్పాట్ యాక్టివ్‌గా ఉంది" + "సెటప్ చేయడానికి ట్యాప్ చేయండి." + "టెథరింగ్ డిజేబుల్ చేయబడింది" + "వివరాల కోసం మీ అడ్మిన్‌ని సంప్రదించండి" + "హాట్‌స్పాట్ & టెథరింగ్ స్థితి" + + + + + diff --git a/Tethering/res/values-th/strings.xml b/Tethering/res/values-th/strings.xml index 1b800565ad..44171c0db8 100644 --- a/Tethering/res/values-th/strings.xml +++ b/Tethering/res/values-th/strings.xml @@ -1,8 +1,29 @@ + + - "การปล่อยสัญญาณหรือฮอตสปอตทำงานอยู่" - "แตะเพื่อตั้งค่า" - "ปิดใช้การเชื่อมต่ออินเทอร์เน็ตผ่านมือถือแล้ว" - "ติดต่อผู้ดูแลระบบเพื่อขอรายละเอียด" + "การเชื่อมต่ออินเทอร์เน็ตผ่านมือถือหรือฮอตสปอตทำงานอยู่" + "แตะเพื่อตั้งค่า" + "ปิดใช้การเชื่อมต่ออินเทอร์เน็ตผ่านมือถือแล้ว" + "ติดต่อผู้ดูแลระบบเพื่อขอรายละเอียด" + "สถานะฮอตสปอตและการเชื่อมต่ออินเทอร์เน็ตผ่านมือถือ" + + + + + diff --git a/Tethering/res/values-tl/strings.xml b/Tethering/res/values-tl/strings.xml index 12863f90e1..7347dd3e62 100644 --- a/Tethering/res/values-tl/strings.xml +++ b/Tethering/res/values-tl/strings.xml @@ -1,8 +1,29 @@ + + - "Pagsasama o aktibong hotspot" - "I-tap upang i-set up." - "Naka-disable ang pag-tether" - "Makipag-ugnayan sa iyong admin para sa mga detalye" + "Aktibo ang pag-tether o hotspot" + "I-tap para i-set up." + "Naka-disable ang pag-tether" + "Makipag-ugnayan sa iyong admin para sa mga detalye" + "Status ng hotspot at pag-tether" + + + + + diff --git a/Tethering/res/values-tr/strings.xml b/Tethering/res/values-tr/strings.xml index bfcf1ace2c..32030f1765 100644 --- a/Tethering/res/values-tr/strings.xml +++ b/Tethering/res/values-tr/strings.xml @@ -1,8 +1,29 @@ + + - "Tethering veya hotspot etkin" - "Ayarlamak için dokunun." - "Tethering devre dışı bırakıldı" - "Ayrıntılı bilgi için yöneticinize başvurun" + "Tethering veya hotspot etkin" + "Ayarlamak için dokunun." + "Tethering devre dışı bırakıldı" + "Ayrıntılı bilgi için yöneticinize başvurun" + "Hotspot ve tethering durumu" + + + + + diff --git a/Tethering/res/values-uk/strings.xml b/Tethering/res/values-uk/strings.xml index 8e159c0723..1ca89b3f78 100644 --- a/Tethering/res/values-uk/strings.xml +++ b/Tethering/res/values-uk/strings.xml @@ -1,8 +1,29 @@ + + - "Прив\'язка чи точка дост. активна" - "Торкніться, щоб налаштувати." - "Використання телефона в режимі модема вимкнено" - "Щоб дізнатися більше, зв’яжіться з адміністратором" + "Модем чи точка доступу активні" + "Натисніть, щоб налаштувати." + "Використання телефона як модема вимкнено" + "Щоб дізнатися більше, зв\'яжіться з адміністратором" + "Статус точки доступу та модема" + + + + + diff --git a/Tethering/res/values-ur/strings.xml b/Tethering/res/values-ur/strings.xml index 89195d4aae..d72c7d4195 100644 --- a/Tethering/res/values-ur/strings.xml +++ b/Tethering/res/values-ur/strings.xml @@ -1,8 +1,29 @@ + + - "ٹیدرنگ یا ہاٹ اسپاٹ فعال" - "سیٹ اپ کرنے کیلئے تھپتھپائیں۔" - "ٹیدرنگ غیر فعال ہے" - "تفصیلات کے لئے اپنے منتظم سے رابطہ کریں" + "ٹیدرنگ یا ہاٹ اسپاٹ فعال" + "سیٹ اپ کرنے کیلئے تھپتھپائیں۔" + "ٹیدرنگ غیر فعال ہے" + "تفصیلات کے لئے اپنے منتظم سے رابطہ کریں" + "ہاٹ اسپاٹ اور ٹیتھرنگ کا اسٹیٹس" + + + + + diff --git a/Tethering/res/values-uz/strings.xml b/Tethering/res/values-uz/strings.xml index 0ac4d4a741..af3b2ebb35 100644 --- a/Tethering/res/values-uz/strings.xml +++ b/Tethering/res/values-uz/strings.xml @@ -1,8 +1,29 @@ + + - "Modem rejimi yoniq" - "Sozlash uchun bosing." - "Modem rejimi faolsizlantirildi" - "Tafsilotlari uchun administratoringizga murojaat qiling" + "Modem rejimi yoki hotspot yoniq" + "Sozlash uchun bosing." + "Modem rejimi faolsizlantirildi" + "Tafsilotlari uchun administratoringizga murojaat qiling" + "Hotspot va modem rejimi holati" + + + + + diff --git a/Tethering/res/values-vi/strings.xml b/Tethering/res/values-vi/strings.xml index 85a4db8aa5..21a0735922 100644 --- a/Tethering/res/values-vi/strings.xml +++ b/Tethering/res/values-vi/strings.xml @@ -1,8 +1,29 @@ + + - "Chức năng điểm truy cập Internet hoặc điểm phát sóng đang hoạt động" - "Nhấn để thiết lập." - "Đã tắt tính năng chia sẻ kết nối" - "Hãy liên hệ với quản trị viên của bạn để biết chi tiết" + "Tính năng chia sẻ Internet hoặc điểm phát sóng đang hoạt động" + "Hãy nhấn để thiết lập." + "Đã tắt tính năng chia sẻ Internet" + "Hãy liên hệ với quản trị viên của bạn để biết chi tiết" + "Trạng thái điểm phát sóng và chia sẻ Internet" + + + + + diff --git a/Tethering/res/values-zh-rCN/strings.xml b/Tethering/res/values-zh-rCN/strings.xml index ff1fe03953..98e3b4b46f 100644 --- a/Tethering/res/values-zh-rCN/strings.xml +++ b/Tethering/res/values-zh-rCN/strings.xml @@ -1,8 +1,29 @@ + + - "网络共享或热点已启用" - "点按即可进行设置。" - "网络共享已停用" - "请与您的管理员联系以了解详情" + "网络共享或热点已启用" + "点按即可设置。" + "网络共享已停用" + "如需了解详情,请与您的管理员联系" + "热点和网络共享状态" + + + + + diff --git a/Tethering/res/values-zh-rHK/strings.xml b/Tethering/res/values-zh-rHK/strings.xml index 0de39fac97..9cafd42dd4 100644 --- a/Tethering/res/values-zh-rHK/strings.xml +++ b/Tethering/res/values-zh-rHK/strings.xml @@ -1,8 +1,29 @@ + + - "已啟用網絡共享或熱點" - "輕按即可設定。" - "網絡共享已停用" - "請聯絡您的管理員以瞭解詳情" + "網絡共享或熱點已啟用" + "輕按即可設定。" + "網絡共享已停用" + "請聯絡您的管理員以瞭解詳情" + "熱點和網絡共享狀態" + + + + + diff --git a/Tethering/res/values-zh-rTW/strings.xml b/Tethering/res/values-zh-rTW/strings.xml index 9a117bbca4..50a50bf7a9 100644 --- a/Tethering/res/values-zh-rTW/strings.xml +++ b/Tethering/res/values-zh-rTW/strings.xml @@ -1,8 +1,29 @@ + + - "網路共用或無線基地台已啟用" - "輕觸即可進行設定。" - "數據連線已停用" - "詳情請洽你的管理員" + "網路共用或無線基地台已啟用" + "輕觸即可進行設定。" + "網路共用已停用" + "詳情請洽你的管理員" + "無線基地台與網路共用狀態" + + + + + diff --git a/Tethering/res/values-zu/strings.xml b/Tethering/res/values-zu/strings.xml index 8fe10d86cb..f210f8726e 100644 --- a/Tethering/res/values-zu/strings.xml +++ b/Tethering/res/values-zu/strings.xml @@ -1,8 +1,29 @@ + + - "Ukusebenzisa njengemodemu noma i-hotspot ephathekayo kuvuliwe" - "Thepha ukuze usethe." - "Ukusebenzisa ifoni njengemodemu kukhutshaziwe" - "Xhumana nomphathi wakho ukuze uthole imininingwane" + "Ukusebenzisa njengemodemu noma i-hotspot ephathekayo kuvuliwe" + "Thepha ukuze usethe." + "Ukusebenzisa ifoni njengemodemu kukhutshaziwe" + "Xhumana nomphathi wakho ukuze uthole imininingwane" + "I-Hotspot nesimo sokusebenzisa ifoni njengemodemu" + + + + + From 9aed13818c6a82f8b1632ea5df53479828f9f704 Mon Sep 17 00:00:00 2001 From: Anton Hansson Date: Tue, 18 Aug 2020 12:52:51 +0100 Subject: [PATCH 188/188] Simplify module visibility post build refactor //visibility:override is no longer needed for impl_library_visibility to override visibility. Removing this allows the defaults module to specify better defaults. - Stub libraries are made publicly visible, via `visibility` - Impl libraries are private by default, but visibility is extended by the modules Bug: 165017290 Test: m Exempt-From-Owner-Approval: build refactor Change-Id: Ibf35bfac5c99a21125f89ba10945f3364217b90f --- Tethering/common/TetheringLib/Android.bp | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/Tethering/common/TetheringLib/Android.bp b/Tethering/common/TetheringLib/Android.bp index c8becce7be..bf643cdcec 100644 --- a/Tethering/common/TetheringLib/Android.bp +++ b/Tethering/common/TetheringLib/Android.bp @@ -16,19 +16,9 @@ java_sdk_library { name: "framework-tethering", defaults: ["framework-module-defaults"], + impl_library_visibility: ["//frameworks/base/packages/Tethering:__subpackages__"], - // Allow access to the stubs from anywhere. - visibility: ["//visibility:public"], - - // Restrict access to implementation library. - impl_library_visibility: [ - "//visibility:override", // Ignore the visibility property. - "//frameworks/base/packages/Tethering:__subpackages__", - ], - - srcs: [ - ":framework-tethering-srcs", - ], + srcs: [":framework-tethering-srcs"], jarjar_rules: "jarjar-rules.txt", installable: true,