Merge changes Idbde7000,Idde3c9d0 into nyc-mr2-dev-plus-aosp
* changes:
DO NOT MERGE ApfFilter: use elapsedRealTime for RA lifetime
DO NOT MERGE ApfFilter: systematically use u8, u16, u32 getters
diff --git a/api/system-current.txt b/api/system-current.txt
index 586bcfb..1fcdf800 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -25762,6 +25762,8 @@
method public android.net.wifi.WifiConfiguration[] getConnectableConfigs();
method public android.net.wifi.WifiConfiguration getConnectedConfig();
method public android.net.wifi.WifiConfiguration getDefaultWifiConfig();
+ method public int getLastSelectedNetworkId();
+ method public long getLastSelectedNetworkTimestamp();
method public android.net.wifi.ScanResult[] getScanResults();
method public void setConnectableConfigs(android.net.wifi.WifiConfiguration[]);
method public void setConnectedConfig(android.net.wifi.WifiConfiguration);
@@ -25775,6 +25777,7 @@
method public android.net.RecommendationRequest.Builder setConnectableConfigs(android.net.wifi.WifiConfiguration[]);
method public android.net.RecommendationRequest.Builder setConnectedWifiConfig(android.net.wifi.WifiConfiguration);
method public android.net.RecommendationRequest.Builder setDefaultWifiConfig(android.net.wifi.WifiConfiguration);
+ method public android.net.RecommendationRequest.Builder setLastSelectedNetwork(int, long);
method public android.net.RecommendationRequest.Builder setScanResults(android.net.wifi.ScanResult[]);
}
@@ -35484,6 +35487,7 @@
field public static final java.lang.String BOOT_COUNT = "boot_count";
field public static final java.lang.String CONTACT_METADATA_SYNC_ENABLED = "contact_metadata_sync_enabled";
field public static final android.net.Uri CONTENT_URI;
+ field public static final java.lang.String CURATE_SAVED_OPEN_NETWORKS = "curate_saved_open_networks";
field public static final java.lang.String DATA_ROAMING = "data_roaming";
field public static final java.lang.String DEBUG_APP = "debug_app";
field public static final java.lang.String DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled";
diff --git a/core/java/android/net/RecommendationRequest.java b/core/java/android/net/RecommendationRequest.java
index a227f18..b89a245 100644
--- a/core/java/android/net/RecommendationRequest.java
+++ b/core/java/android/net/RecommendationRequest.java
@@ -37,6 +37,8 @@
private final WifiConfiguration mDefaultConfig;
private WifiConfiguration mConnectedConfig;
private WifiConfiguration[] mConnectableConfigs;
+ private final int mLastSelectedNetworkId;
+ private final long mLastSelectedNetworkTimestamp;
/**
* Builder class for constructing {@link RecommendationRequest} instances.
@@ -48,17 +50,9 @@
private WifiConfiguration mDefaultConfig;
private WifiConfiguration mConnectedConfig;
private WifiConfiguration[] mConnectableConfigs;
+ private int mLastSelectedNetworkId;
+ private long mLastSelectedTimestamp;
- /**
- * @param scanResults the array of {@link ScanResult}s the recommendation must be
- * constrained to i.e. if a non-null wifi config recommendation is
- * returned then it must be able to connect to one of the networks in
- * the results list.
- *
- * If the array is {@code null} or empty then there is no constraint.
- *
- * @return this
- */
public Builder setScanResults(ScanResult[] scanResults) {
mScanResults = scanResults;
return this;
@@ -89,7 +83,20 @@
* @return this
*/
public Builder setConnectableConfigs(WifiConfiguration[] connectableConfigs) {
- mConnectableConfigs = connectableConfigs;
+ this.mConnectableConfigs = connectableConfigs;
+ return this;
+ }
+
+ /**
+ * @param networkId The {@link WifiConfiguration#networkId} of the last user selected
+ * network.
+ * @param timestamp The {@link android.os.SystemClock#elapsedRealtime()} when the user
+ * selected {@code networkId}.
+ * @return this
+ */
+ public Builder setLastSelectedNetwork(int networkId, long timestamp) {
+ this.mLastSelectedNetworkId = networkId;
+ this.mLastSelectedTimestamp = timestamp;
return this;
}
@@ -97,10 +104,8 @@
* @return a new {@link RecommendationRequest} instance
*/
public RecommendationRequest build() {
- return new RecommendationRequest(mScanResults,
- mDefaultConfig,
- mConnectedConfig,
- mConnectableConfigs);
+ return new RecommendationRequest(mScanResults, mDefaultConfig, mConnectedConfig,
+ mConnectableConfigs, mLastSelectedNetworkId, mLastSelectedTimestamp);
}
}
@@ -154,15 +159,35 @@
mConnectableConfigs = connectableConfigs;
}
+ /**
+ * @return The {@link WifiConfiguration#networkId} of the last user selected network.
+ * {@code 0} if not set.
+ */
+ public int getLastSelectedNetworkId() {
+ return mLastSelectedNetworkId;
+ }
+
+ /**
+ * @return The {@link android.os.SystemClock#elapsedRealtime()} when the user selected
+ * {@link #getLastSelectedNetworkId()}. {@code 0} if not set.
+ */
+ public long getLastSelectedNetworkTimestamp() {
+ return mLastSelectedNetworkTimestamp;
+ }
+
@VisibleForTesting
RecommendationRequest(ScanResult[] scanResults,
WifiConfiguration defaultWifiConfig,
WifiConfiguration connectedWifiConfig,
- WifiConfiguration[] connectableConfigs) {
+ WifiConfiguration[] connectableConfigs,
+ int lastSelectedNetworkId,
+ long lastSelectedNetworkTimestamp) {
mScanResults = scanResults;
mDefaultConfig = defaultWifiConfig;
mConnectedConfig = connectedWifiConfig;
mConnectableConfigs = connectableConfigs;
+ mLastSelectedNetworkId = lastSelectedNetworkId;
+ mLastSelectedNetworkTimestamp = lastSelectedNetworkTimestamp;
}
protected RecommendationRequest(Parcel in) {
@@ -190,6 +215,9 @@
} else {
mConnectableConfigs = null;
}
+
+ mLastSelectedNetworkId = in.readInt();
+ mLastSelectedNetworkTimestamp = in.readLong();
}
@Override
@@ -220,7 +248,8 @@
dest.writeInt(0);
}
-
+ dest.writeInt(mLastSelectedNetworkId);
+ dest.writeLong(mLastSelectedNetworkTimestamp);
}
public static final Creator<RecommendationRequest> CREATOR =
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 4b130ed1..20de370 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.opengl.EGL14;
import android.os.SystemProperties;
import android.util.Log;
@@ -34,6 +35,23 @@
private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0";
public static void setupGraphicsEnvironment(Context context) {
+ chooseDriver(context);
+
+ // Now that we've figured out which driver to use for this process, load and initialize it.
+ // This can take multiple frame periods, and it would otherwise happen as part of the first
+ // frame, increasing first-frame latency. Starting it here, as a low-priority background
+ // thread, means that it's usually done long before we start drawing the first frame,
+ // without significantly disrupting other activity launch work.
+ Thread eglInitThread = new Thread(
+ () -> {
+ Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
+ EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
+ },
+ "EGL Init");
+ eglInitThread.start();
+ }
+
+ private static void chooseDriver(Context context) {
String driverPackageName = SystemProperties.get(PROPERTY_GFX_DRIVER);
if (driverPackageName == null || driverPackageName.isEmpty()) {
return;
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index f6e6ad6..b5ab908 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -27,6 +27,8 @@
import android.util.SparseArray;
import android.util.SparseBooleanArray;
+import libcore.util.SneakyThrow;
+
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileDescriptor;
@@ -249,6 +251,7 @@
private static final int EX_NETWORK_MAIN_THREAD = -6;
private static final int EX_UNSUPPORTED_OPERATION = -7;
private static final int EX_SERVICE_SPECIFIC = -8;
+ private static final int EX_PARCELABLE = -9;
private static final int EX_HAS_REPLY_HEADER = -128; // special; see below
// EX_TRANSACTION_FAILED is used exclusively in native code.
// see libbinder's binder/Status.h
@@ -1555,7 +1558,12 @@
*/
public final void writeException(Exception e) {
int code = 0;
- if (e instanceof SecurityException) {
+ if (e instanceof Parcelable
+ && (e.getClass().getClassLoader() == Parcelable.class.getClassLoader())) {
+ // We only send Parcelable exceptions that are in the
+ // BootClassLoader to ensure that the receiver can unpack them
+ code = EX_PARCELABLE;
+ } else if (e instanceof SecurityException) {
code = EX_SECURITY;
} else if (e instanceof BadParcelableException) {
code = EX_BAD_PARCELABLE;
@@ -1581,8 +1589,20 @@
throw new RuntimeException(e);
}
writeString(e.getMessage());
- if (e instanceof ServiceSpecificException) {
- writeInt(((ServiceSpecificException)e).errorCode);
+ switch (code) {
+ case EX_SERVICE_SPECIFIC:
+ writeInt(((ServiceSpecificException) e).errorCode);
+ break;
+ case EX_PARCELABLE:
+ // Write parceled exception prefixed by length
+ final int sizePosition = dataPosition();
+ writeInt(0);
+ writeParcelable((Parcelable) e, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+ final int payloadPosition = dataPosition();
+ setDataPosition(sizePosition);
+ writeInt(payloadPosition - sizePosition);
+ setDataPosition(payloadPosition);
+ break;
}
}
@@ -1680,6 +1700,13 @@
*/
public final void readException(int code, String msg) {
switch (code) {
+ case EX_PARCELABLE:
+ if (readInt() > 0) {
+ SneakyThrow.sneakyThrow(
+ (Exception) readParcelable(Parcelable.class.getClassLoader()));
+ } else {
+ throw new RuntimeException(msg + " [missing Parcelable]");
+ }
case EX_SECURITY:
throw new SecurityException(msg);
case EX_BAD_PARCELABLE:
diff --git a/core/java/android/os/ParcelableException.java b/core/java/android/os/ParcelableException.java
new file mode 100644
index 0000000..d84d629
--- /dev/null
+++ b/core/java/android/os/ParcelableException.java
@@ -0,0 +1,88 @@
+/*
+ * 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.os;
+
+import java.io.IOException;
+
+/**
+ * Wrapper class that offers to transport typical {@link Throwable} across a
+ * {@link Binder} call. This class is typically used to transport exceptions
+ * that cannot be modified to add {@link Parcelable} behavior, such as
+ * {@link IOException}.
+ * <ul>
+ * <li>The wrapped throwable must be defined as system class (that is, it must
+ * be in the same {@link ClassLoader} as {@link Parcelable}).
+ * <li>The wrapped throwable must support the
+ * {@link Throwable#Throwable(String)} constructor.
+ * <li>The receiver side must catch any thrown {@link ParcelableException} and
+ * call {@link #maybeRethrow(Class)} for all expected exception types.
+ * </ul>
+ *
+ * @hide
+ */
+public final class ParcelableException extends RuntimeException implements Parcelable {
+ public ParcelableException(Throwable t) {
+ super(t);
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T extends Throwable> void maybeRethrow(Class<T> clazz) throws T {
+ if (clazz.isAssignableFrom(getCause().getClass())) {
+ throw (T) getCause();
+ }
+ }
+
+ /** {@hide} */
+ public static Throwable readFromParcel(Parcel in) {
+ final String name = in.readString();
+ final String msg = in.readString();
+ try {
+ final Class<?> clazz = Class.forName(name, true, Parcelable.class.getClassLoader());
+ return (Throwable) clazz.getConstructor(String.class).newInstance(msg);
+ } catch (ReflectiveOperationException e) {
+ throw new RuntimeException(name + ": " + msg);
+ }
+ }
+
+ /** {@hide} */
+ public static void writeToParcel(Parcel out, Throwable t) {
+ out.writeString(t.getClass().getName());
+ out.writeString(t.getMessage());
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ writeToParcel(dest, getCause());
+ }
+
+ public static final Creator<ParcelableException> CREATOR = new Creator<ParcelableException>() {
+ @Override
+ public ParcelableException createFromParcel(Parcel source) {
+ return new ParcelableException(readFromParcel(source));
+ }
+
+ @Override
+ public ParcelableException[] newArray(int size) {
+ return new ParcelableException[size];
+ }
+ };
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index eb04e54..896dcb1 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7696,6 +7696,16 @@
"network_recommendations_enabled";
/**
+ * Value to specify if the Wi-Fi Framework should defer to
+ * {@link com.android.server.NetworkScoreService} for evaluating saved open networks.
+ *
+ * Type: int (0 for false, 1 for true)
+ * @hide
+ */
+ @SystemApi
+ public static final String CURATE_SAVED_OPEN_NETWORKS = "curate_saved_open_networks";
+
+ /**
* The number of milliseconds the {@link com.android.server.NetworkScoreService}
* will give a recommendation request to complete before returning a default response.
*
diff --git a/core/java/android/provider/VoicemailContract.java b/core/java/android/provider/VoicemailContract.java
index 5099eeb..7d5941e 100644
--- a/core/java/android/provider/VoicemailContract.java
+++ b/core/java/android/provider/VoicemailContract.java
@@ -30,6 +30,7 @@
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.Voicemail;
+
import java.util.List;
/**
@@ -318,6 +319,48 @@
public static final String LAST_MODIFIED = "last_modified";
/**
+ * Flag to indicate the voicemail was backed up. The value will be 1 if backed up, 0 if
+ * not.
+ *
+ * <P>Type: INTEGER (boolean)</P>
+ *
+ * @hide
+ */
+ public static final String BACKED_UP = "backed_up";
+
+ /**
+ * Flag to indicate the voicemail was restored from a backup. The value will be 1 if
+ * restored, 0 if not.
+ *
+ * <P>Type: INTEGER (boolean)</P>
+ *
+ * @hide
+ */
+ public static final String RESTORED = "restored";
+
+ /**
+ * Flag to indicate the voicemail was marked as archived. Archived voicemail should not be
+ * deleted even if it no longer exist on the server. The value will be 1 if archived true, 0
+ * if not.
+ *
+ * <P>Type: INTEGER (boolean)</P>
+ *
+ * @hide
+ */
+ public static final String ARCHIVED = "archived";
+
+ /**
+ * Flag to indicate the voicemail is a OMTP voicemail handled by the {@link
+ * android.telephony.VisualVoicemailService}. The UI should only show OMTP voicemails from
+ * the current visual voicemail package.
+ *
+ * <P>Type: INTEGER (boolean)</P>
+ *
+ * @hide
+ */
+ public static final String IS_OMTP_VOICEMAIL = "is_omtp_voicmail";
+
+ /**
* A convenience method to build voicemail URI specific to a source package by appending
* {@link VoicemailContract#PARAM_KEY_SOURCE_PACKAGE} param to the base URI.
*/
diff --git a/core/java/android/util/ExceptionUtils.java b/core/java/android/util/ExceptionUtils.java
index f5d515d..da0b609 100644
--- a/core/java/android/util/ExceptionUtils.java
+++ b/core/java/android/util/ExceptionUtils.java
@@ -16,6 +16,8 @@
package android.util;
+import android.os.ParcelableException;
+
import java.io.IOException;
/**
@@ -24,19 +26,13 @@
* @hide
*/
public class ExceptionUtils {
- // TODO: longer term these should be replaced with first-class
- // Parcel.read/writeException() and AIDL support, but for now do this using
- // a nasty hack.
-
- private static final String PREFIX_IO = "\u2603";
-
public static RuntimeException wrap(IOException e) {
- throw new IllegalStateException(PREFIX_IO + e.getMessage());
+ throw new ParcelableException(e);
}
public static void maybeUnwrapIOException(RuntimeException e) throws IOException {
- if ((e instanceof IllegalStateException) && e.getMessage().startsWith(PREFIX_IO)) {
- throw new IOException(e.getMessage().substring(PREFIX_IO.length()));
+ if (e instanceof ParcelableException) {
+ ((ParcelableException) e).maybeRethrow(IOException.class);
}
}
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index ef5231c..6ad7667 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -25,7 +25,6 @@
import android.icu.text.DecimalFormatSymbols;
import android.icu.util.ULocale;
import android.net.LocalServerSocket;
-import android.opengl.EGL14;
import android.os.IInstalld;
import android.os.Process;
import android.os.RemoteException;
@@ -121,9 +120,6 @@
Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadResources");
preloadResources();
Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
- Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadOpenGL");
- preloadOpenGL();
- Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
preloadSharedLibraries();
preloadTextResources();
// Ask the WebViewFactory to do any initialization that must run in the zygote process,
@@ -164,12 +160,6 @@
System.loadLibrary("jnigraphics");
}
- private static void preloadOpenGL() {
- if (!SystemProperties.getBoolean(PROPERTY_DISABLE_OPENGL_PRELOADING, false)) {
- EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
- }
- }
-
private static void preloadTextResources() {
Hyphenator.init();
TextView.preloadFontCache();
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 3039d4f..c2a4800 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -395,7 +395,7 @@
<string name="network_logging_notification_title">Network traffic is being monitored</string>
<!-- Content text for a notification. Tapping opens a dialog with more information on network
logging. [CHAR LIMIT=NONE]-->
- <string name="network_logging_notification_text">Tap for more details</string>
+ <string name="network_logging_notification_text">Tap to learn more</string>
<!-- Factory reset warning dialog strings--> <skip />
<!-- Shows up in the dialog's title to warn about an impeding factory reset. [CHAR LIMIT=NONE] -->
diff --git a/core/tests/coretests/src/android/net/RecommendationRequestTest.java b/core/tests/coretests/src/android/net/RecommendationRequestTest.java
index 39c1691..bd25500 100644
--- a/core/tests/coretests/src/android/net/RecommendationRequestTest.java
+++ b/core/tests/coretests/src/android/net/RecommendationRequestTest.java
@@ -3,6 +3,7 @@
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.os.Parcel;
+import android.os.SystemClock;
import android.test.AndroidTestCase;
public class RecommendationRequestTest extends AndroidTestCase {
@@ -10,6 +11,8 @@
private WifiConfiguration mDefaultConfig;
private WifiConfiguration mConnectedConfig;
private WifiConfiguration[] mConnectableConfigs;
+ private int mLastSelectedNetworkId;
+ private long mLastSelectedNetworkTimestamp;
@Override
public void setUp() throws Exception {
@@ -35,6 +38,8 @@
mConnectedConfig = new WifiConfiguration();
mConnectedConfig.SSID = "connected_config";
mConnectableConfigs = new WifiConfiguration[] {mDefaultConfig, mConnectedConfig};
+ mLastSelectedNetworkId = 5;
+ mLastSelectedNetworkTimestamp = SystemClock.elapsedRealtime();
}
public void testParceling() throws Exception {
@@ -43,6 +48,7 @@
.setScanResults(mScanResults)
.setConnectedWifiConfig(mConnectedConfig)
.setConnectableConfigs(mConnectableConfigs)
+ .setLastSelectedNetwork(mLastSelectedNetworkId, mLastSelectedNetworkTimestamp)
.build();
RecommendationRequest parceled = passThroughParcel(request);
@@ -60,6 +66,8 @@
for (int i = 0; i < parceledConfigs.length; i++) {
assertEquals(mConnectableConfigs[i].SSID, parceledConfigs[i].SSID);
}
+ assertEquals(mLastSelectedNetworkId, parceled.getLastSelectedNetworkId());
+ assertEquals(mLastSelectedNetworkTimestamp, parceled.getLastSelectedNetworkTimestamp());
}
public void testParceling_nullScanResults() throws Exception {
@@ -82,6 +90,16 @@
assertNull(parceledConfigs);
}
+ public void testParceling_unsetLastSelectedNetwork() throws Exception {
+ RecommendationRequest request = new RecommendationRequest.Builder()
+ .build();
+
+ RecommendationRequest parceled = passThroughParcel(request);
+
+ assertEquals(0, parceled.getLastSelectedNetworkId());
+ assertEquals(0, parceled.getLastSelectedNetworkTimestamp());
+ }
+
private RecommendationRequest passThroughParcel(RecommendationRequest request) {
Parcel p = Parcel.obtain();
RecommendationRequest output = null;
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 3627c29..bd3bfbc 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -891,4 +891,10 @@
<!-- Content description for drawer menu button [CHAR_LIMIT=30]-->
<string name="content_description_menu_button">Menu</string>
+ <!-- Label for carrier demo mode factory reset confirmation dialog. [CHAR LIMIT=40] -->
+ <string name="retail_demo_reset_message">Enter password to perform factory reset in demo mode</string>
+ <!-- Label for positive button on carrier demo mode factory reset confirmation dialog [CHAR LIMIT=40] -->
+ <string name="retail_demo_reset_next">Next</string>
+ <!-- Title for carrier demo mode factory reset confirmation dialog. [CHAR LIMIT=40] -->
+ <string name="retail_demo_reset_title">Password required</string>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 337529a..221795b 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1019,11 +1019,10 @@
<!-- Monitoring dialog title for normal devices [CHAR LIMIT=35]-->
<string name="monitoring_title">Network monitoring</string>
- <!-- STOPSHIP(b/33655277) Monitoring strings still need to be finalized and approved -->
- <!-- Monitoring dialog subtitle for the section describing VPN [CHAR LIMIT=TODO]-->
+ <!-- Monitoring dialog subtitle for the section describing VPN [CHAR LIMIT=35]-->
<string name="monitoring_subtitle_vpn">VPN</string>
- <!-- Monitoring dialog subtitle for the section describing network logging [CHAR LIMIT=TODO]-->
+ <!-- Monitoring dialog subtitle for the section describing network logging [CHAR LIMIT=35]-->
<string name="monitoring_subtitle_network_logging">Network Logging</string>
<!-- Monitoring dialog disable vpn button [CHAR LIMIT=30] -->
@@ -1033,7 +1032,7 @@
<string name="disconnect_vpn">Disconnect VPN</string>
<!-- Monitoring dialog device owner body text [CHAR LIMIT=400] -->
- <string name="monitoring_description_device_owned">Your device is managed by <xliff:g id="organization">%1$s</xliff:g>.\n\nYour administrator can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information. For more information, contact your administrator.</string>
+ <string name="monitoring_description_device_owned">Your device is managed by <xliff:g id="organization">%1$s</xliff:g>.\n\nYour admin can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information. For more information, contact your admin.</string>
<!-- Monitoring dialog: Part of text body explaining that a VPN is connected and what it can do, for devices managed by a Device Owner app [CHAR LIMIT=130] -->
<string name="monitoring_description_do_body_vpn">You\'re connected to <xliff:g id="vpn_app">%1$s</xliff:g>, which can monitor your network activity, including emails, apps, and websites.</string>
@@ -1041,21 +1040,21 @@
<!-- Monitoring dialog: Space that separates the VPN body text and the "Open VPN Settings" link that follows it. [CHAR LIMIT=5] -->
<string name="monitoring_description_vpn_settings_separator">" "</string>
- <!-- Monitoring dialog: Link to open the VPN settings page [CHAR LIMIT=TODO] -->
+ <!-- Monitoring dialog: Link to open the VPN settings page [CHAR LIMIT=60] -->
<string name="monitoring_description_vpn_settings">Open VPN Settings</string>
- <!-- Monitoring dialog: Network logging text [CHAR LIMIT=TODO] -->
- <string name="monitoring_description_network_logging">Your admin has turned on network logging, which monitors traffic on your device.\n\nFor more information contact your admin.</string>
+ <!-- Monitoring dialog: Network logging text [CHAR LIMIT=400] -->
+ <string name="monitoring_description_network_logging">Your admin has turned on network logging, which monitors traffic on your device.\n\nFor more information, contact your admin.</string>
<!-- Monitoring dialog VPN text [CHAR LIMIT=400] -->
<string name="monitoring_description_vpn">You gave an app permission to set up a VPN connection.\n\nThis app can monitor your device and network activity, including emails, apps, and websites.</string>
<!-- Monitoring dialog VPN with device owner text [CHAR LIMIT=400] -->
- <string name="monitoring_description_vpn_device_owned">Your device is managed by <xliff:g id="organization">%1$s</xliff:g>.\n\nYour administrator can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.\n\nYou\'re connected to a VPN, which can monitor your network activity, including emails, apps, and websites.\n\nFor more information, contact your administrator.</string>
+ <string name="monitoring_description_vpn_device_owned">Your device is managed by <xliff:g id="organization">%1$s</xliff:g>.\n\nYour admin can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.\n\nYou\'re connected to a VPN, which can monitor your network activity, including emails, apps, and websites.\n\nFor more information, contact your admin.</string>
<!-- Monitoring dialog VPN with profile owner text [CHAR LIMIT=400] -->
- <string name="monitoring_description_vpn_profile_owned">Your work profile is managed by <xliff:g id="organization">%1$s</xliff:g>.\n\nYour administrator is capable of monitoring your network activity including emails, apps, and websites.\n\nFor more information, contact your administrator.\n\nYou\'re also connected to a VPN, which can monitor your network activity.</string>
+ <string name="monitoring_description_vpn_profile_owned">Your work profile is managed by <xliff:g id="organization">%1$s</xliff:g>.\n\nYour admin is capable of monitoring your network activity including emails, apps, and websites.\n\nFor more information, contact your admin.\n\nYou\'re also connected to a VPN, which can monitor your network activity.</string>
<!-- Name for a generic legacy VPN connection [CHAR LIMIT=20] -->
<string name="legacy_vpn_name">VPN</string>
@@ -1070,13 +1069,13 @@
<string name="branded_monitoring_description_app_personal">You\'re connected to <xliff:g id="application">%1$s</xliff:g>, which can monitor your personal network activity, including emails, apps, and websites.</string>
<!-- Monitoring dialog text for single app (inside work profile) [CHAR LIMIT=400] -->
- <string name="monitoring_description_app_work">Your work profile is managed by <xliff:g id="organization">%1$s</xliff:g>. It is connected to <xliff:g id="application">%2$s</xliff:g>, which can monitor your work network activity, including emails, apps, and websites.\n\nFor more information, contact your administrator.</string>
+ <string name="monitoring_description_app_work">Your work profile is managed by <xliff:g id="organization">%1$s</xliff:g>. It is connected to <xliff:g id="application">%2$s</xliff:g>, which can monitor your work network activity, including emails, apps, and websites.\n\nFor more information, contact your admin.</string>
<!-- Monitoring dialog text for multiple apps (in personal and work profiles) [CHAR LIMIT=400] -->
<string name="monitoring_description_app_personal_work">Your work profile is managed by <xliff:g id="organization">%1$s</xliff:g>. It is connected to <xliff:g id="application_work">%2$s</xliff:g>, which can monitor your work network activity, including emails, apps, and websites.\n\nYou\'re also connected to <xliff:g id="application_personal">%3$s</xliff:g>, which can monitor your personal network activity.</string>
<!-- Monitoring dialog text for single app (with device owner) [CHAR LIMIT=400] -->
- <string name="monitoring_description_vpn_app_device_owned">Your device is managed by <xliff:g id="organization">%1$s</xliff:g>.\n\nYour administrator can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.\n\nYou\'re connected to <xliff:g id="application">%2$s</xliff:g>, which can monitor your network activity, including emails, apps, and websites.\n\nFor more information, contact your administrator.</string>
+ <string name="monitoring_description_vpn_app_device_owned">Your device is managed by <xliff:g id="organization">%1$s</xliff:g>.\n\nYour admin can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.\n\nYou\'re connected to <xliff:g id="application">%2$s</xliff:g>, which can monitor your network activity, including emails, apps, and websites.\n\nFor more information, contact your admin.</string>
<!-- Indication on the keyguard that appears when the user disables trust agents until the next time they unlock manually. [CHAR LIMIT=NONE] -->
<string name="keyguard_indication_trust_disabled">Device will stay locked until you manually unlock</string>
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index b3627ad..5549ced 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -3130,10 +3130,7 @@
Settings.Global.TETHER_SUPPORTED, defaultVal) != 0)
&& !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING);
return tetherEnabledInSettings && mUserManager.isAdminUser() &&
- ((mTethering.getTetherableUsbRegexs().length != 0 ||
- mTethering.getTetherableWifiRegexs().length != 0 ||
- mTethering.getTetherableBluetoothRegexs().length != 0) &&
- mTethering.getUpstreamIfaceTypes().length != 0);
+ mTethering.hasTetherableConfiguration();
}
@Override
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
index 90f507c..40b0e2e 100644
--- a/services/core/java/com/android/server/SystemServiceManager.java
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -35,6 +35,8 @@
private final Context mContext;
private boolean mSafeMode;
+ private boolean mRuntimeRestarted;
+ private boolean mFirstBoot;
// Services that should receive lifecycle events.
private final ArrayList<SystemService> mServices = new ArrayList<SystemService>();
@@ -246,6 +248,28 @@
}
/**
+ * @return true if runtime was restarted, false if it's normal boot
+ */
+ public boolean isRuntimeRestarted() {
+ return mRuntimeRestarted;
+ }
+
+ void setRuntimeRestarted(boolean runtimeRestarted) {
+ mRuntimeRestarted = runtimeRestarted;
+ }
+
+ /**
+ * @return true if it's first boot after OTA
+ */
+ public boolean isFirstBoot() {
+ return mFirstBoot;
+ }
+
+ void setFirstBoot(boolean firstBoot) {
+ mFirstBoot = firstBoot;
+ }
+
+ /**
* Outputs the state of this manager to the System log.
*/
public void dump() {
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 1ef3728..19eeff0 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -239,11 +239,12 @@
// storage is already unlocked.
if (uss.setState(STATE_BOOTING, STATE_RUNNING_LOCKED)) {
getUserManagerInternal().setUserState(userId, uss.state);
-
- int uptimeSeconds = (int)(SystemClock.elapsedRealtime() / 1000);
- MetricsLogger.histogram(mService.mContext, "framework_locked_boot_completed",
- uptimeSeconds);
-
+ if (!mService.mSystemServiceManager.isRuntimeRestarted()
+ && !mService.mSystemServiceManager.isFirstBoot()) {
+ int uptimeSeconds = (int)(SystemClock.elapsedRealtime() / 1000);
+ MetricsLogger.histogram(mService.mContext, "framework_locked_boot_completed",
+ uptimeSeconds);
+ }
Intent intent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED, null);
intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
@@ -415,8 +416,13 @@
}
Slog.d(TAG, "Sending BOOT_COMPLETE user #" + userId);
- int uptimeSeconds = (int)(SystemClock.elapsedRealtime() / 1000);
- MetricsLogger.histogram(mService.mContext, "framework_boot_completed", uptimeSeconds);
+ if (!mService.mSystemServiceManager.isRuntimeRestarted()
+ && !mService.mSystemServiceManager.isFirstBoot()) {
+
+ int uptimeSeconds = (int) (SystemClock.elapsedRealtime() / 1000);
+ MetricsLogger
+ .histogram(mService.mContext, "framework_boot_completed", uptimeSeconds);
+ }
final Intent bootIntent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
bootIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
bootIntent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 0c80166..b82dd60 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -16,6 +16,11 @@
package com.android.server.connectivity;
+import static android.hardware.usb.UsbManager.USB_CONNECTED;
+import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS;
+import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
+
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -108,11 +113,7 @@
private static final SparseArray<String> sMagicDecoderRing =
MessageUtils.findMessageNames(messageClasses);
- // TODO - remove both of these - should be part of interface inspection/selection stuff
- private String[] mTetherableUsbRegexs;
- private String[] mTetherableWifiRegexs;
- private String[] mTetherableBluetoothRegexs;
- private Collection<Integer> mUpstreamIfaceTypes;
+ private volatile TetheringConfiguration mConfig;
// used to synchronize public access to members
private final Object mPublicSync;
@@ -121,10 +122,6 @@
private static final Integer HIPRI_TYPE = new Integer(ConnectivityManager.TYPE_MOBILE_HIPRI);
private static final Integer DUN_TYPE = new Integer(ConnectivityManager.TYPE_MOBILE_DUN);
- // if we have to connect to mobile, what APN type should we use? Calculated by examining the
- // upstream type list and the DUN_REQUIRED secure-setting
- private int mPreferredUpstreamMobileApn = ConnectivityManager.TYPE_NONE;
-
private final INetworkManagementService mNMService;
private final INetworkStatsService mStatsService;
private final INetworkPolicyManager mPolicyManager;
@@ -150,24 +147,6 @@
private static final ComponentName TETHER_SERVICE = ComponentName.unflattenFromString(Resources
.getSystem().getString(com.android.internal.R.string.config_wifi_tether_enable));
- // USB is 192.168.42.1 and 255.255.255.0
- // Wifi is 192.168.43.1 and 255.255.255.0
- // BT is limited to max default of 5 connections. 192.168.44.1 to 192.168.48.1
- // with 255.255.255.0
- // P2P is 192.168.49.1 and 255.255.255.0
-
- private String[] mDhcpRange;
- private static final String[] DHCP_DEFAULT_RANGE = {
- "192.168.42.2", "192.168.42.254", "192.168.43.2", "192.168.43.254",
- "192.168.44.2", "192.168.44.254", "192.168.45.2", "192.168.45.254",
- "192.168.46.2", "192.168.46.254", "192.168.47.2", "192.168.47.254",
- "192.168.48.2", "192.168.48.254", "192.168.49.2", "192.168.49.254",
- };
-
- private String[] mDefaultDnsServers;
- private static final String DNS_DEFAULT_SERVER1 = "8.8.8.8";
- private static final String DNS_DEFAULT_SERVER2 = "8.8.4.4";
-
private final StateMachine mTetherMasterSM;
private final UpstreamNetworkMonitor mUpstreamNetworkMonitor;
private String mCurrentUpstreamIface;
@@ -208,27 +187,16 @@
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
- mContext.registerReceiver(mStateReceiver, filter);
+ mContext.registerReceiver(mStateReceiver, filter, null, mTetherMasterSM.getHandler());
filter = new IntentFilter();
filter.addAction(Intent.ACTION_MEDIA_SHARED);
filter.addAction(Intent.ACTION_MEDIA_UNSHARED);
filter.addDataScheme("file");
- mContext.registerReceiver(mStateReceiver, filter);
-
- mDhcpRange = context.getResources().getStringArray(
- com.android.internal.R.array.config_tether_dhcp_range);
- if ((mDhcpRange.length == 0) || (mDhcpRange.length % 2 ==1)) {
- mDhcpRange = DHCP_DEFAULT_RANGE;
- }
+ mContext.registerReceiver(mStateReceiver, filter, null, mTetherMasterSM.getHandler());
// load device config info
updateConfiguration();
-
- // TODO - remove and rely on real notifications of the current iface
- mDefaultDnsServers = new String[2];
- mDefaultDnsServers[0] = DNS_DEFAULT_SERVER1;
- mDefaultDnsServers[1] = DNS_DEFAULT_SERVER2;
}
// We can't do this once in the Tethering() constructor and cache the value, because the
@@ -237,30 +205,8 @@
return (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
}
- void updateConfiguration() {
- String[] tetherableUsbRegexs = mContext.getResources().getStringArray(
- com.android.internal.R.array.config_tether_usb_regexs);
- String[] tetherableWifiRegexs = mContext.getResources().getStringArray(
- com.android.internal.R.array.config_tether_wifi_regexs);
- String[] tetherableBluetoothRegexs = mContext.getResources().getStringArray(
- com.android.internal.R.array.config_tether_bluetooth_regexs);
-
- int ifaceTypes[] = mContext.getResources().getIntArray(
- com.android.internal.R.array.config_tether_upstream_types);
- Collection<Integer> upstreamIfaceTypes = new ArrayList<>();
- for (int i : ifaceTypes) {
- upstreamIfaceTypes.add(new Integer(i));
- }
-
- synchronized (mPublicSync) {
- mTetherableUsbRegexs = tetherableUsbRegexs;
- mTetherableWifiRegexs = tetherableWifiRegexs;
- mTetherableBluetoothRegexs = tetherableBluetoothRegexs;
- mUpstreamIfaceTypes = upstreamIfaceTypes;
- }
-
- // check if the upstream type list needs to be modified due to secure-settings
- checkDunRequired();
+ private void updateConfiguration() {
+ mConfig = new TetheringConfiguration(mContext);
}
@Override
@@ -300,39 +246,14 @@
interfaceStatusChanged(iface, up);
}
- private boolean isUsb(String iface) {
- synchronized (mPublicSync) {
- for (String regex : mTetherableUsbRegexs) {
- if (iface.matches(regex)) return true;
- }
- return false;
- }
- }
-
- private boolean isWifi(String iface) {
- synchronized (mPublicSync) {
- for (String regex : mTetherableWifiRegexs) {
- if (iface.matches(regex)) return true;
- }
- return false;
- }
- }
-
- private boolean isBluetooth(String iface) {
- synchronized (mPublicSync) {
- for (String regex : mTetherableBluetoothRegexs) {
- if (iface.matches(regex)) return true;
- }
- return false;
- }
- }
-
private int ifaceNameToType(String iface) {
- if (isWifi(iface)) {
+ final TetheringConfiguration cfg = mConfig;
+
+ if (cfg.isWifi(iface)) {
return ConnectivityManager.TETHERING_WIFI;
- } else if (isUsb(iface)) {
+ } else if (cfg.isUsb(iface)) {
return ConnectivityManager.TETHERING_USB;
- } else if (isBluetooth(iface)) {
+ } else if (cfg.isBluetooth(iface)) {
return ConnectivityManager.TETHERING_BLUETOOTH;
}
return ConnectivityManager.TETHERING_INVALID;
@@ -662,6 +583,8 @@
boolean usbTethered = false;
boolean bluetoothTethered = false;
+ final TetheringConfiguration cfg = mConfig;
+
synchronized (mPublicSync) {
for (int i = 0; i < mTetherStates.size(); i++) {
TetherState tetherState = mTetherStates.valueAt(i);
@@ -671,11 +594,11 @@
} else if (tetherState.mLastState == IControlsTethering.STATE_AVAILABLE) {
availableList.add(iface);
} else if (tetherState.mLastState == IControlsTethering.STATE_TETHERED) {
- if (isUsb(iface)) {
+ if (cfg.isUsb(iface)) {
usbTethered = true;
- } else if (isWifi(iface)) {
+ } else if (cfg.isWifi(iface)) {
wifiTethered = true;
- } else if (isBluetooth(iface)) {
+ } else if (cfg.isBluetooth(iface)) {
bluetoothTethered = true;
}
activeList.add(iface);
@@ -779,69 +702,84 @@
private class StateReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context content, Intent intent) {
- String action = intent.getAction();
- if (action == null) { return; }
+ final String action = intent.getAction();
+ if (action == null) return;
+
if (action.equals(UsbManager.ACTION_USB_STATE)) {
- synchronized (Tethering.this.mPublicSync) {
- boolean usbConnected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
- mRndisEnabled = intent.getBooleanExtra(UsbManager.USB_FUNCTION_RNDIS, false);
- // start tethering if we have a request pending
- if (usbConnected && mRndisEnabled && mUsbTetherRequested) {
- tetherMatchingInterfaces(true, ConnectivityManager.TETHERING_USB);
- }
- mUsbTetherRequested = false;
- }
+ handleUsbAction(intent);
} else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
- NetworkInfo networkInfo = (NetworkInfo)intent.getParcelableExtra(
- ConnectivityManager.EXTRA_NETWORK_INFO);
- if (networkInfo != null &&
- networkInfo.getDetailedState() != NetworkInfo.DetailedState.FAILED) {
- if (VDBG) Log.d(TAG, "Tethering got CONNECTIVITY_ACTION");
- mTetherMasterSM.sendMessage(TetherMasterSM.CMD_UPSTREAM_CHANGED);
- }
+ handleConnectivityAction(intent);
} else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) {
- synchronized (Tethering.this.mPublicSync) {
- int curState = intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_STATE,
- WifiManager.WIFI_AP_STATE_DISABLED);
- 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:
- // When the AP comes up and we've been requested to tether it, do so.
- if (mWifiTetherRequested) {
- tetherMatchingInterfaces(true, ConnectivityManager.TETHERING_WIFI);
- }
- break;
- case WifiManager.WIFI_AP_STATE_DISABLED:
- case WifiManager.WIFI_AP_STATE_DISABLING:
- case WifiManager.WIFI_AP_STATE_FAILED:
- default:
- if (DBG) {
- Log.d(TAG, "Canceling WiFi tethering request - AP_STATE=" +
- curState);
- }
- // Tell appropriate interface state machines that they should tear
- // themselves down.
- for (int i = 0; i < mTetherStates.size(); i++) {
- TetherInterfaceStateMachine tism =
- mTetherStates.valueAt(i).mStateMachine;
- if (tism.interfaceType() == ConnectivityManager.TETHERING_WIFI) {
- tism.sendMessage(
- TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED);
- break; // There should be at most one of these.
- }
- }
- // 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.
- mWifiTetherRequested = false;
- break;
- }
- }
+ handleWifiApAction(intent);
} else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
updateConfiguration();
}
}
+
+ private void handleConnectivityAction(Intent intent) {
+ final NetworkInfo networkInfo = (NetworkInfo)intent.getParcelableExtra(
+ ConnectivityManager.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 rndisEnabled = intent.getBooleanExtra(USB_FUNCTION_RNDIS, false);
+ synchronized (Tethering.this.mPublicSync) {
+ mRndisEnabled = rndisEnabled;
+ // start tethering if we have a request pending
+ if (usbConnected && mRndisEnabled && mUsbTetherRequested) {
+ tetherMatchingInterfaces(true, ConnectivityManager.TETHERING_USB);
+ }
+ mUsbTetherRequested = false;
+ }
+ }
+
+ private void handleWifiApAction(Intent intent) {
+ final int curState = intent.getIntExtra(EXTRA_WIFI_AP_STATE, WIFI_AP_STATE_DISABLED);
+ 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:
+ // When the AP comes up and we've been requested to tether it, do so.
+ if (mWifiTetherRequested) {
+ tetherMatchingInterfaces(true, ConnectivityManager.TETHERING_WIFI);
+ }
+ break;
+ case WifiManager.WIFI_AP_STATE_DISABLED:
+ case WifiManager.WIFI_AP_STATE_DISABLING:
+ case WifiManager.WIFI_AP_STATE_FAILED:
+ default:
+ if (DBG) {
+ Log.d(TAG, "Canceling WiFi tethering request - AP_STATE=" +
+ curState);
+ }
+ // Tell appropriate interface state machines that they should tear
+ // themselves down.
+ for (int i = 0; i < mTetherStates.size(); i++) {
+ TetherInterfaceStateMachine tism =
+ mTetherStates.valueAt(i).mStateMachine;
+ if (tism.interfaceType() == ConnectivityManager.TETHERING_WIFI) {
+ tism.sendMessage(
+ TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED);
+ break; // There should be at most one of these.
+ }
+ }
+ // 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.
+ mWifiTetherRequested = false;
+ break;
+ }
+ }
+ }
}
private void tetherMatchingInterfaces(boolean enable, int interfaceType) {
@@ -875,26 +813,38 @@
}
}
- // TODO - return copies so people can't tamper
+ public TetheringConfiguration getTetheringConfiguration() {
+ return mConfig;
+ }
+
+ public 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();
+
+ return hasDownstreamConfiguration && hasUpstreamConfiguration;
+ }
+
+ // TODO - update callers to use getTetheringConfiguration(),
+ // which has only final members.
public String[] getTetherableUsbRegexs() {
- return mTetherableUsbRegexs;
+ return copy(mConfig.tetherableUsbRegexs);
}
public String[] getTetherableWifiRegexs() {
- return mTetherableWifiRegexs;
+ return copy(mConfig.tetherableWifiRegexs);
}
public String[] getTetherableBluetoothRegexs() {
- return mTetherableBluetoothRegexs;
+ return copy(mConfig.tetherableBluetoothRegexs);
}
public int setUsbTethering(boolean enable) {
if (VDBG) Log.d(TAG, "setUsbTethering(" + enable + ")");
UsbManager usbManager = mContext.getSystemService(UsbManager.class);
- if (usbManager == null) {
- return enable ? ConnectivityManager.TETHER_ERROR_MASTER_ERROR
- : ConnectivityManager.TETHER_ERROR_NO_ERROR;
- }
synchronized (mPublicSync) {
if (enable) {
@@ -925,61 +875,6 @@
return ConnectivityManager.TETHER_ERROR_NO_ERROR;
}
- public int[] getUpstreamIfaceTypes() {
- int values[];
- synchronized (mPublicSync) {
- updateConfiguration(); // TODO - remove?
- values = new int[mUpstreamIfaceTypes.size()];
- Iterator<Integer> iterator = mUpstreamIfaceTypes.iterator();
- for (int i=0; i < mUpstreamIfaceTypes.size(); i++) {
- values[i] = iterator.next();
- }
- }
- return values;
- }
-
- private void checkDunRequired() {
- int secureSetting = 2;
- TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
- if (tm != null) {
- secureSetting = tm.getTetherApnRequired();
- }
- synchronized (mPublicSync) {
- // 2 = not set, 0 = DUN not required, 1 = DUN required
- if (secureSetting != 2) {
- int requiredApn = (secureSetting == 1 ?
- ConnectivityManager.TYPE_MOBILE_DUN :
- ConnectivityManager.TYPE_MOBILE_HIPRI);
- if (requiredApn == ConnectivityManager.TYPE_MOBILE_DUN) {
- while (mUpstreamIfaceTypes.contains(MOBILE_TYPE)) {
- mUpstreamIfaceTypes.remove(MOBILE_TYPE);
- }
- while (mUpstreamIfaceTypes.contains(HIPRI_TYPE)) {
- mUpstreamIfaceTypes.remove(HIPRI_TYPE);
- }
- if (mUpstreamIfaceTypes.contains(DUN_TYPE) == false) {
- mUpstreamIfaceTypes.add(DUN_TYPE);
- }
- } else {
- while (mUpstreamIfaceTypes.contains(DUN_TYPE)) {
- mUpstreamIfaceTypes.remove(DUN_TYPE);
- }
- if (mUpstreamIfaceTypes.contains(MOBILE_TYPE) == false) {
- mUpstreamIfaceTypes.add(MOBILE_TYPE);
- }
- if (mUpstreamIfaceTypes.contains(HIPRI_TYPE) == false) {
- mUpstreamIfaceTypes.add(HIPRI_TYPE);
- }
- }
- }
- if (mUpstreamIfaceTypes.contains(DUN_TYPE)) {
- mPreferredUpstreamMobileApn = ConnectivityManager.TYPE_MOBILE_DUN;
- } else {
- mPreferredUpstreamMobileApn = ConnectivityManager.TYPE_MOBILE_HIPRI;
- }
- }
- }
-
// TODO review API - maybe return ArrayList<String> here and below?
public String[] getTetheredIfaces() {
ArrayList<String> list = new ArrayList<String>();
@@ -1008,7 +903,7 @@
}
public String[] getTetheredDhcpRanges() {
- return mDhcpRange;
+ return mConfig.dhcpRanges;
}
public String[] getErroredIfaces() {
@@ -1083,7 +978,7 @@
private final ArrayList<TetherInterfaceStateMachine> mNotifyList;
private final IPv6TetheringCoordinator mIPv6TetheringCoordinator;
- private int mPreviousMobileApn = ConnectivityManager.TYPE_NONE;
+ private int mPreviousMobileType = ConnectivityManager.TYPE_NONE;
private static final int UPSTREAM_SETTLE_TIME_MS = 10000;
@@ -1118,13 +1013,13 @@
return false;
}
- protected boolean turnOnUpstreamMobileConnection(int apnType) {
+ protected boolean requestUpstreamMobileConnection(int apnType) {
if (apnType == ConnectivityManager.TYPE_NONE) { return false; }
- if (apnType != mPreviousMobileApn) {
+ if (apnType != mPreviousMobileType) {
// Unregister any previous mobile upstream callback because
// this request, if any, will be different.
- turnOffUpstreamMobileConnection();
+ unrequestUpstreamMobileConnection();
}
if (mUpstreamNetworkMonitor.mobileNetworkRequested()) {
@@ -1136,28 +1031,29 @@
case ConnectivityManager.TYPE_MOBILE_DUN:
case ConnectivityManager.TYPE_MOBILE:
case ConnectivityManager.TYPE_MOBILE_HIPRI:
- mPreviousMobileApn = apnType;
+ mPreviousMobileType = apnType;
break;
default:
return false;
}
- // TODO: This should be called by the code that observes
- // configuration changes, once the above code in this function
- // is simplified (i.e. eradicated).
- mUpstreamNetworkMonitor.mobileUpstreamRequiresDun(
+ // TODO: Replace this with a call to pass the current tethering
+ // configuration to mUpstreamNetworkMonitor and let it handle
+ // choosing APN type accordingly.
+ mUpstreamNetworkMonitor.updateMobileRequiresDun(
apnType == ConnectivityManager.TYPE_MOBILE_DUN);
mUpstreamNetworkMonitor.registerMobileNetworkRequest();
return true;
}
- protected void turnOffUpstreamMobileConnection() {
+ protected void unrequestUpstreamMobileConnection() {
mUpstreamNetworkMonitor.releaseMobileNetworkRequest();
- mPreviousMobileApn = ConnectivityManager.TYPE_NONE;
+ mPreviousMobileType = ConnectivityManager.TYPE_NONE;
}
protected boolean turnOnMasterTetherSettings() {
+ final TetheringConfiguration cfg = mConfig;
try {
mNMService.setIpForwardingEnabled(true);
} catch (Exception e) {
@@ -1165,11 +1061,11 @@
return false;
}
try {
- mNMService.startTethering(mDhcpRange);
+ mNMService.startTethering(cfg.dhcpRanges);
} catch (Exception e) {
try {
mNMService.stopTethering();
- mNMService.startTethering(mDhcpRange);
+ mNMService.startTethering(cfg.dhcpRanges);
} catch (Exception ee) {
transitionTo(mStartTetheringErrorState);
return false;
@@ -1202,29 +1098,31 @@
updateConfiguration(); // TODO - remove?
- synchronized (mPublicSync) {
- if (VDBG) {
- Log.d(TAG, "chooseUpstreamType has upstream iface types:");
- for (Integer netType : mUpstreamIfaceTypes) {
- Log.d(TAG, " " + netType);
- }
- }
-
- for (Integer netType : mUpstreamIfaceTypes) {
- NetworkInfo info = cm.getNetworkInfo(netType.intValue());
- // TODO: if the network is suspended we should consider
- // that to be the same as connected here.
- if ((info != null) && info.isConnected()) {
- upType = netType.intValue();
- break;
- }
+ final TetheringConfiguration cfg = mConfig;
+ if (VDBG) {
+ Log.d(TAG, "chooseUpstreamType has upstream iface types:");
+ for (Integer netType : cfg.preferredUpstreamIfaceTypes) {
+ Log.d(TAG, " " + netType);
}
}
+ for (Integer netType : cfg.preferredUpstreamIfaceTypes) {
+ NetworkInfo info = cm.getNetworkInfo(netType.intValue());
+ // TODO: if the network is suspended we should consider
+ // that to be the same as connected here.
+ if ((info != null) && info.isConnected()) {
+ upType = netType.intValue();
+ break;
+ }
+ }
+
+ final int preferredUpstreamMobileApn = cfg.isDunRequired
+ ? ConnectivityManager.TYPE_MOBILE_DUN
+ : ConnectivityManager.TYPE_MOBILE_HIPRI;
if (DBG) {
Log.d(TAG, "chooseUpstreamType(" + tryCell + "),"
+ " preferredApn="
- + ConnectivityManager.getNetworkTypeName(mPreferredUpstreamMobileApn)
+ + ConnectivityManager.getNetworkTypeName(preferredUpstreamMobileApn)
+ ", got type="
+ ConnectivityManager.getNetworkTypeName(upType));
}
@@ -1233,11 +1131,11 @@
case ConnectivityManager.TYPE_MOBILE_DUN:
case ConnectivityManager.TYPE_MOBILE_HIPRI:
// If we're on DUN, put our own grab on it.
- turnOnUpstreamMobileConnection(upType);
+ requestUpstreamMobileConnection(upType);
break;
case ConnectivityManager.TYPE_NONE:
if (tryCell &&
- turnOnUpstreamMobileConnection(mPreferredUpstreamMobileApn)) {
+ requestUpstreamMobileConnection(preferredUpstreamMobileApn)) {
// We think mobile should be coming up; don't set a retry.
} else {
sendMessageDelayed(CMD_RETRY_UPSTREAM, UPSTREAM_SETTLE_TIME_MS);
@@ -1250,7 +1148,7 @@
* If we found NONE we don't want to do this as we want any previous
* requests to keep trying to bring up something we can use.
*/
- turnOffUpstreamMobileConnection();
+ unrequestUpstreamMobileConnection();
break;
}
@@ -1295,7 +1193,8 @@
}
protected void setDnsForwarders(final Network network, final LinkProperties lp) {
- String[] dnsServers = mDefaultDnsServers;
+ // TODO: Set v4 and/or v6 DNS per available connectivity.
+ String[] dnsServers = mConfig.defaultIPv4DNS;
final Collection<InetAddress> dnses = lp.getDnsServers();
// TODO: Properly support the absence of DNS servers.
if (dnses != null && !dnses.isEmpty()) {
@@ -1330,96 +1229,127 @@
}
}
- private final AtomicInteger mSimBcastGenerationNumber = new AtomicInteger(0);
- private SimChangeBroadcastReceiver mBroadcastReceiver = null;
+ private class SimChangeListener {
+ private final Context mContext;
+ private final AtomicInteger mSimBcastGenerationNumber;
+ private BroadcastReceiver mBroadcastReceiver;
- private void startListeningForSimChanges() {
- if (DBG) Log.d(TAG, "startListeningForSimChanges");
- if (mBroadcastReceiver == null) {
+ SimChangeListener(Context ctx) {
+ mContext = ctx;
+ mSimBcastGenerationNumber = new AtomicInteger(0);
+ }
+
+ public int generationNumber() {
+ return mSimBcastGenerationNumber.get();
+ }
+
+ public void startListening() {
+ if (DBG) Log.d(TAG, "startListening for SIM changes");
+
+ if (mBroadcastReceiver != null) return;
+
mBroadcastReceiver = new SimChangeBroadcastReceiver(
mSimBcastGenerationNumber.incrementAndGet());
final IntentFilter filter = new IntentFilter();
filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
- mContext.registerReceiver(mBroadcastReceiver, filter);
+ mContext.registerReceiver(mBroadcastReceiver, filter, null,
+ mTetherMasterSM.getHandler());
}
- }
- private void stopListeningForSimChanges() {
- if (DBG) Log.d(TAG, "stopListeningForSimChanges");
- if (mBroadcastReceiver != null) {
+ public void stopListening() {
+ if (DBG) Log.d(TAG, "stopListening for SIM changes");
+
+ if (mBroadcastReceiver == null) return;
+
mSimBcastGenerationNumber.incrementAndGet();
mContext.unregisterReceiver(mBroadcastReceiver);
mBroadcastReceiver = null;
}
- }
- class SimChangeBroadcastReceiver extends BroadcastReceiver {
- // used to verify this receiver is still current
- final private int mGenerationNumber;
-
- // we're interested in edge-triggered LOADED notifications, so
- // ignore LOADED unless we saw an ABSENT state first
- private boolean mSimAbsentSeen = false;
-
- public SimChangeBroadcastReceiver(int generationNumber) {
- super();
- mGenerationNumber = generationNumber;
+ public boolean hasMobileHotspotProvisionApp() {
+ try {
+ if (!mContext.getResources().getString(com.android.internal.R.string.
+ config_mobile_hotspot_provision_app_no_ui).isEmpty()) {
+ Log.d(TAG, "re-evaluate provisioning");
+ return true;
+ }
+ } catch (Resources.NotFoundException e) {}
+ Log.d(TAG, "no prov-check needed for new SIM");
+ return false;
}
- @Override
- public void onReceive(Context context, Intent intent) {
- if (DBG) {
- Log.d(TAG, "simchange mGenerationNumber=" + mGenerationNumber +
- ", current generationNumber=" + mSimBcastGenerationNumber.get());
- }
- if (mGenerationNumber != mSimBcastGenerationNumber.get()) return;
+ private boolean isSimCardAbsent(String state) {
+ return IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(state);
+ }
- final String state =
- intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
+ private boolean isSimCardLoaded(String state) {
+ return IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(state);
+ }
- Log.d(TAG, "got Sim changed to state " + state + ", mSimAbsentSeen=" +
- mSimAbsentSeen);
- if (!mSimAbsentSeen && IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(state)) {
- mSimAbsentSeen = true;
+ private void startProvisionIntent(int tetherType) {
+ final Intent startProvIntent = new Intent();
+ startProvIntent.putExtra(ConnectivityManager.EXTRA_ADD_TETHER_TYPE, tetherType);
+ startProvIntent.putExtra(ConnectivityManager.EXTRA_RUN_PROVISION, true);
+ startProvIntent.setComponent(TETHER_SERVICE);
+ mContext.startServiceAsUser(startProvIntent, UserHandle.CURRENT);
+ }
+
+ private class SimChangeBroadcastReceiver extends BroadcastReceiver {
+ // used to verify this receiver is still current
+ final private int mGenerationNumber;
+
+ // we're interested in edge-triggered LOADED notifications, so
+ // ignore LOADED unless we saw an ABSENT state first
+ private boolean mSimAbsentSeen = false;
+
+ public SimChangeBroadcastReceiver(int generationNumber) {
+ mGenerationNumber = generationNumber;
}
- if (mSimAbsentSeen && IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(state)) {
- mSimAbsentSeen = false;
- try {
- if (mContext.getResources().getString(com.android.internal.R.string.
- config_mobile_hotspot_provision_app_no_ui).isEmpty() == false) {
- ArrayList<Integer> tethered = new ArrayList<Integer>();
- synchronized (mPublicSync) {
- for (int i = 0; i < mTetherStates.size(); i++) {
- TetherState tetherState = mTetherStates.valueAt(i);
- if (tetherState.mLastState !=
- IControlsTethering.STATE_TETHERED) {
- continue; // Skip interfaces that aren't tethered.
- }
- String iface = mTetherStates.keyAt(i);
- int interfaceType = ifaceNameToType(iface);
- if (interfaceType != ConnectivityManager.TETHERING_INVALID) {
- tethered.add(new Integer(interfaceType));
- }
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final int currentGenerationNumber = mSimBcastGenerationNumber.get();
+
+ if (DBG) {
+ Log.d(TAG, "simchange mGenerationNumber=" + mGenerationNumber +
+ ", current generationNumber=" + currentGenerationNumber);
+ }
+ if (mGenerationNumber != currentGenerationNumber) return;
+
+ final String state = intent.getStringExtra(
+ IccCardConstants.INTENT_KEY_ICC_STATE);
+ Log.d(TAG, "got Sim changed to state " + state + ", mSimAbsentSeen=" +
+ mSimAbsentSeen);
+
+ if (isSimCardAbsent(state)) {
+ if (!mSimAbsentSeen) mSimAbsentSeen = true;
+ return;
+ }
+
+ if (isSimCardLoaded(state) && mSimAbsentSeen) {
+ mSimAbsentSeen = false;
+
+ if (!hasMobileHotspotProvisionApp()) return;
+
+ ArrayList<Integer> tethered = new ArrayList<Integer>();
+ synchronized (mPublicSync) {
+ for (int i = 0; i < mTetherStates.size(); i++) {
+ TetherState tetherState = mTetherStates.valueAt(i);
+ if (tetherState.mLastState != IControlsTethering.STATE_TETHERED) {
+ continue; // Skip interfaces that aren't tethered.
+ }
+ String iface = mTetherStates.keyAt(i);
+ int interfaceType = ifaceNameToType(iface);
+ if (interfaceType != ConnectivityManager.TETHERING_INVALID) {
+ tethered.add(new Integer(interfaceType));
}
}
- for (int tetherType : tethered) {
- Intent startProvIntent = new Intent();
- startProvIntent.putExtra(
- ConnectivityManager.EXTRA_ADD_TETHER_TYPE, tetherType);
- startProvIntent.putExtra(
- ConnectivityManager.EXTRA_RUN_PROVISION, true);
- startProvIntent.setComponent(TETHER_SERVICE);
- mContext.startServiceAsUser(startProvIntent, UserHandle.CURRENT);
- }
- Log.d(TAG, "re-evaluate provisioning");
- } else {
- Log.d(TAG, "no prov-check needed for new SIM");
}
- } catch (Resources.NotFoundException e) {
- Log.d(TAG, "no prov-check needed for new SIM");
- // not defined, do nothing
+
+ for (int tetherType : tethered) {
+ startProvisionIntent(tetherType);
+ }
}
}
}
@@ -1455,12 +1385,13 @@
}
class TetherModeAliveState extends TetherMasterUtilState {
+ final SimChangeListener simChange = new SimChangeListener(mContext);
boolean mTryCell = true;
@Override
public void enter() {
// TODO: examine if we should check the return value.
turnOnMasterTetherSettings(); // may transition us out
- startListeningForSimChanges();
+ simChange.startListening();
mUpstreamNetworkMonitor.start();
mTryCell = true; // better try something first pass or crazy tests cases will fail
@@ -1470,9 +1401,9 @@
@Override
public void exit() {
- turnOffUpstreamMobileConnection();
+ unrequestUpstreamMobileConnection();
mUpstreamNetworkMonitor.stop();
- stopListeningForSimChanges();
+ simChange.stopListening();
notifyTetheredOfNewUpstreamIface(null);
handleNewUpstreamNetworkState(null);
}
@@ -1672,9 +1603,10 @@
pw.println("Tethering:");
pw.increaseIndent();
- pw.print("mUpstreamIfaceTypes:");
+ final TetheringConfiguration cfg = mConfig;
+ pw.print("preferredUpstreamIfaceTypes:");
synchronized (mPublicSync) {
- for (Integer netType : mUpstreamIfaceTypes) {
+ for (Integer netType : cfg.preferredUpstreamIfaceTypes) {
pw.print(" " + ConnectivityManager.getNetworkTypeName(netType));
}
pw.println();
@@ -1754,4 +1686,123 @@
mTetherStates.put(iface, tetherState);
tetherState.mStateMachine.start();
}
+
+ private static String[] copy(String[] strarray) {
+ return Arrays.copyOf(strarray, strarray.length);
+ }
+
+ private static class TetheringConfiguration {
+ private static final int DUN_NOT_REQUIRED = 0;
+ private static final int DUN_REQUIRED = 1;
+ private static final int DUN_UNSPECIFIED = 2;
+
+ // USB is 192.168.42.1 and 255.255.255.0
+ // Wifi is 192.168.43.1 and 255.255.255.0
+ // BT is limited to max default of 5 connections. 192.168.44.1 to 192.168.48.1
+ // with 255.255.255.0
+ // P2P is 192.168.49.1 and 255.255.255.0
+ private static final String[] DHCP_DEFAULT_RANGE = {
+ "192.168.42.2", "192.168.42.254", "192.168.43.2", "192.168.43.254",
+ "192.168.44.2", "192.168.44.254", "192.168.45.2", "192.168.45.254",
+ "192.168.46.2", "192.168.46.254", "192.168.47.2", "192.168.47.254",
+ "192.168.48.2", "192.168.48.254", "192.168.49.2", "192.168.49.254",
+ };
+
+ private final String[] DEFAULT_IPV4_DNS = {"8.8.4.4", "8.8.8.8"};
+
+ public final String[] tetherableUsbRegexs;
+ public final String[] tetherableWifiRegexs;
+ public final String[] tetherableBluetoothRegexs;
+ public final boolean isDunRequired;
+ public final Collection<Integer> preferredUpstreamIfaceTypes;
+ public final String[] dhcpRanges;
+ public final String[] defaultIPv4DNS;
+
+ public TetheringConfiguration(Context ctx) {
+ tetherableUsbRegexs = ctx.getResources().getStringArray(
+ com.android.internal.R.array.config_tether_usb_regexs);
+ tetherableWifiRegexs = ctx.getResources().getStringArray(
+ com.android.internal.R.array.config_tether_wifi_regexs);
+ tetherableBluetoothRegexs = ctx.getResources().getStringArray(
+ com.android.internal.R.array.config_tether_bluetooth_regexs);
+
+ isDunRequired = checkDunRequired(ctx);
+ preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(ctx, isDunRequired);
+
+ dhcpRanges = getDhcpRanges(ctx);
+ defaultIPv4DNS = copy(DEFAULT_IPV4_DNS);
+ }
+
+ public boolean isUsb(String iface) {
+ for (String regex : tetherableUsbRegexs) {
+ if (iface.matches(regex)) return true;
+ }
+ return false;
+ }
+
+ public boolean isWifi(String iface) {
+ for (String regex : tetherableWifiRegexs) {
+ if (iface.matches(regex)) return true;
+ }
+ return false;
+ }
+
+ public boolean isBluetooth(String iface) {
+ for (String regex : tetherableBluetoothRegexs) {
+ if (iface.matches(regex)) return true;
+ }
+ return false;
+ }
+
+ private static boolean checkDunRequired(Context ctx) {
+ final TelephonyManager tm = ctx.getSystemService(TelephonyManager.class);
+ final int secureSetting =
+ (tm != null) ? tm.getTetherApnRequired() : DUN_UNSPECIFIED;
+ return (secureSetting == DUN_REQUIRED);
+ }
+
+ private static Collection<Integer> getUpstreamIfaceTypes(Context ctx, boolean requiresDun) {
+ final int ifaceTypes[] = ctx.getResources().getIntArray(
+ com.android.internal.R.array.config_tether_upstream_types);
+ final ArrayList<Integer> upstreamIfaceTypes = new ArrayList<>(ifaceTypes.length);
+ for (int i : ifaceTypes) {
+ upstreamIfaceTypes.add(i);
+ }
+
+ // Fix up upstream interface types for DUN or mobile.
+ // TODO: Perform this filtering step in the above for loop.
+ if (requiresDun) {
+ while (upstreamIfaceTypes.contains(MOBILE_TYPE)) {
+ upstreamIfaceTypes.remove(MOBILE_TYPE);
+ }
+ while (upstreamIfaceTypes.contains(HIPRI_TYPE)) {
+ upstreamIfaceTypes.remove(HIPRI_TYPE);
+ }
+ if (!upstreamIfaceTypes.contains(DUN_TYPE)) {
+ upstreamIfaceTypes.add(DUN_TYPE);
+ }
+ } else {
+ while (upstreamIfaceTypes.contains(DUN_TYPE)) {
+ upstreamIfaceTypes.remove(DUN_TYPE);
+ }
+ if (!upstreamIfaceTypes.contains(MOBILE_TYPE)) {
+ upstreamIfaceTypes.add(MOBILE_TYPE);
+ }
+ if (!upstreamIfaceTypes.contains(HIPRI_TYPE)) {
+ upstreamIfaceTypes.add(HIPRI_TYPE);
+ }
+ }
+
+ return upstreamIfaceTypes;
+ }
+
+ private static String[] getDhcpRanges(Context ctx) {
+ final String[] fromResource = ctx.getResources().getStringArray(
+ com.android.internal.R.array.config_tether_dhcp_range);
+ if ((fromResource.length > 0) && (fromResource.length % 2 == 0)) {
+ return fromResource;
+ }
+ return copy(DHCP_DEFAULT_RANGE);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
index 4c950de..23481dc 100644
--- a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
@@ -109,7 +109,7 @@
mNetworkMap.clear();
}
- public void mobileUpstreamRequiresDun(boolean dunRequired) {
+ public void updateMobileRequiresDun(boolean dunRequired) {
final boolean valueChanged = (mDunRequired != dunRequired);
mDunRequired = dunRequired;
if (valueChanged && mobileNetworkRequested()) {
@@ -123,7 +123,10 @@
}
public void registerMobileNetworkRequest() {
- if (mMobileNetworkCallback != null) return;
+ if (mMobileNetworkCallback != null) {
+ Log.e(TAG, "registerMobileNetworkRequest() already registered");
+ return;
+ }
final NetworkRequest.Builder builder = new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
@@ -139,11 +142,10 @@
// Therefore, to avoid duplicate notifications, we only register a no-op.
mMobileNetworkCallback = new NetworkCallback();
- // TODO: Change the timeout from 0 (no onUnavailable callback) to use some
- // moderate callback time (once timeout callbacks are implemented). This might
- // be useful for updating some UI. Additionally, we should definitely log a
- // message to aid in any subsequent debugging
- if (DBG) Log.d(TAG, "requesting mobile upstream network: " + mobileUpstreamRequest);
+ // TODO: Change the timeout from 0 (no onUnavailable callback) to some
+ // moderate callback timeout. This might be useful for updating some UI.
+ // Additionally, we log a message to aid in any subsequent debugging.
+ Log.d(TAG, "requesting mobile upstream network: " + mobileUpstreamRequest);
// The following use of the legacy type system cannot be removed until
// after upstream selection no longer finds networks by legacy type.
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index c38cfb5..8c4941d 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -208,6 +208,7 @@
private boolean mOnlyCore;
private boolean mFirstBoot;
+ private final boolean mRuntimeRestart;
/**
* Start the sensor service.
@@ -224,6 +225,8 @@
public SystemServer() {
// Check for factory test mode.
mFactoryTestMode = FactoryTest.getMode();
+ // Remember if it's runtime restart(when sys.boot_completed is already set) or reboot
+ mRuntimeRestart = "1".equals(SystemProperties.get("sys.boot_completed"));
}
private void run() {
@@ -323,6 +326,8 @@
// Create the system service manager.
mSystemServiceManager = new SystemServiceManager(mSystemContext);
+ mSystemServiceManager.setRuntimeRestarted(mRuntimeRestart);
+ mSystemServiceManager.setFirstBoot(mFirstBoot);
LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
@@ -836,6 +841,15 @@
}
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ // Wifi Service must be started first for wifi-related services.
+ mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
+ mSystemServiceManager.startService(
+ "com.android.server.wifi.scanner.WifiScanningService");
+
+ if (!disableRtt) {
+ mSystemServiceManager.startService("com.android.server.wifi.RttService");
+ }
+
if (context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_WIFI_AWARE)) {
mSystemServiceManager.startService(WIFI_AWARE_SERVICE_CLASS);
@@ -847,13 +861,6 @@
PackageManager.FEATURE_WIFI_DIRECT)) {
mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS);
}
- mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
- mSystemServiceManager.startService(
- "com.android.server.wifi.scanner.WifiScanningService");
-
- if (!disableRtt) {
- mSystemServiceManager.startService("com.android.server.wifi.RttService");
- }
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_ETHERNET) ||
mPackageManager.hasSystemFeature(PackageManager.FEATURE_USB_HOST)) {
diff --git a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
index 00420e9..1e67769 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
@@ -22,6 +22,7 @@
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.Mockito.reset;
import android.content.Context;
@@ -69,12 +70,13 @@
@Test
public void testDoesNothingBeforeStarted() {
- UpstreamNetworkMonitor unm = new UpstreamNetworkMonitor(null, null, EVENT_UNM_UPDATE);
- assertFalse(unm.mobileNetworkRequested());
- // Given a null Context, and therefore a null ConnectivityManager,
- // these would cause an exception, if they actually attempted anything.
- unm.mobileUpstreamRequiresDun(true);
- unm.mobileUpstreamRequiresDun(false);
+ assertTrue(mCM.hasNoCallbacks());
+ assertFalse(mUNM.mobileNetworkRequested());
+
+ mUNM.updateMobileRequiresDun(true);
+ assertTrue(mCM.hasNoCallbacks());
+ mUNM.updateMobileRequiresDun(false);
+ assertTrue(mCM.hasNoCallbacks());
}
@Test
@@ -85,7 +87,7 @@
assertEquals(1, mCM.trackingDefault.size());
mUNM.stop();
- assertTrue(mCM.isEmpty());
+ assertTrue(mCM.hasNoCallbacks());
}
@Test
@@ -97,11 +99,11 @@
assertTrue(mCM.isListeningForDun());
mUNM.stop();
- assertTrue(mCM.isEmpty());
+ assertTrue(mCM.hasNoCallbacks());
}
@Test
- public void testCanRequestMobileNetwork() throws Exception {
+ public void testRequestsMobileNetwork() throws Exception {
assertFalse(mUNM.mobileNetworkRequested());
assertEquals(0, mCM.requested.size());
@@ -109,25 +111,22 @@
assertFalse(mUNM.mobileNetworkRequested());
assertEquals(0, mCM.requested.size());
- mUNM.mobileUpstreamRequiresDun(false);
+ mUNM.updateMobileRequiresDun(false);
assertFalse(mUNM.mobileNetworkRequested());
assertEquals(0, mCM.requested.size());
mUNM.registerMobileNetworkRequest();
assertTrue(mUNM.mobileNetworkRequested());
- assertEquals(1, mCM.requested.size());
- assertEquals(1, mCM.legacyTypeMap.size());
- assertEquals(Integer.valueOf(TYPE_MOBILE_HIPRI),
- mCM.legacyTypeMap.values().iterator().next());
+ assertUpstreamTypeRequested(TYPE_MOBILE_HIPRI);
assertFalse(mCM.isDunRequested());
mUNM.stop();
assertFalse(mUNM.mobileNetworkRequested());
- assertTrue(mCM.isEmpty());
+ assertTrue(mCM.hasNoCallbacks());
}
@Test
- public void testCanRequestDunNetwork() throws Exception {
+ public void testRequestsDunNetwork() throws Exception {
assertFalse(mUNM.mobileNetworkRequested());
assertEquals(0, mCM.requested.size());
@@ -135,21 +134,50 @@
assertFalse(mUNM.mobileNetworkRequested());
assertEquals(0, mCM.requested.size());
- mUNM.mobileUpstreamRequiresDun(true);
+ mUNM.updateMobileRequiresDun(true);
assertFalse(mUNM.mobileNetworkRequested());
assertEquals(0, mCM.requested.size());
mUNM.registerMobileNetworkRequest();
assertTrue(mUNM.mobileNetworkRequested());
- assertEquals(1, mCM.requested.size());
- assertEquals(1, mCM.legacyTypeMap.size());
- assertEquals(Integer.valueOf(TYPE_MOBILE_DUN),
- mCM.legacyTypeMap.values().iterator().next());
+ assertUpstreamTypeRequested(TYPE_MOBILE_DUN);
assertTrue(mCM.isDunRequested());
mUNM.stop();
assertFalse(mUNM.mobileNetworkRequested());
- assertTrue(mCM.isEmpty());
+ assertTrue(mCM.hasNoCallbacks());
+ }
+
+ @Test
+ public void testUpdateMobileRequiredDun() throws Exception {
+ mUNM.start();
+
+ // Test going from no-DUN to DUN correctly re-registers callbacks.
+ mUNM.updateMobileRequiresDun(false);
+ mUNM.registerMobileNetworkRequest();
+ assertTrue(mUNM.mobileNetworkRequested());
+ assertUpstreamTypeRequested(TYPE_MOBILE_HIPRI);
+ assertFalse(mCM.isDunRequested());
+ mUNM.updateMobileRequiresDun(true);
+ assertTrue(mUNM.mobileNetworkRequested());
+ assertUpstreamTypeRequested(TYPE_MOBILE_DUN);
+ assertTrue(mCM.isDunRequested());
+
+ // Test going from DUN to no-DUN correctly re-registers callbacks.
+ mUNM.updateMobileRequiresDun(false);
+ assertTrue(mUNM.mobileNetworkRequested());
+ assertUpstreamTypeRequested(TYPE_MOBILE_HIPRI);
+ assertFalse(mCM.isDunRequested());
+
+ mUNM.stop();
+ assertFalse(mUNM.mobileNetworkRequested());
+ }
+
+ private void assertUpstreamTypeRequested(int upstreamType) throws Exception {
+ assertEquals(1, mCM.requested.size());
+ assertEquals(1, mCM.legacyTypeMap.size());
+ assertEquals(Integer.valueOf(upstreamType),
+ mCM.legacyTypeMap.values().iterator().next());
}
private static class TestConnectivityManager extends ConnectivityManager {
@@ -162,7 +190,7 @@
super(ctx, svc);
}
- boolean isEmpty() {
+ boolean hasNoCallbacks() {
return trackingDefault.isEmpty() &&
listening.isEmpty() &&
requested.isEmpty() &&
@@ -225,6 +253,8 @@
} else if (requested.containsKey(cb)) {
requested.remove(cb);
legacyTypeMap.remove(cb);
+ } else {
+ fail("Unexpected callback removed");
}
assertFalse(trackingDefault.contains(cb));