Merge "MediaMetadata2: Add radio frequency and callsigns"
diff --git a/core/java/android/security/keystore/recovery/KeyChainSnapshot.java b/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
index d4ec472..f043d6a 100644
--- a/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
+++ b/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
@@ -290,7 +290,6 @@
                     "entryRecoveryData");
             Preconditions.checkNotNull(mInstance.mEncryptedRecoveryKeyBlob);
             Preconditions.checkNotNull(mInstance.mServerParams);
-            Preconditions.checkNotNull(mInstance.mPublicKey);
             return mInstance;
         }
     }
diff --git a/core/tests/coretests/src/android/security/keystore/recovery/KeyChainSnapshotTest.java b/core/tests/coretests/src/android/security/keystore/recovery/KeyChainSnapshotTest.java
new file mode 100644
index 0000000..8d6fbd5
--- /dev/null
+++ b/core/tests/coretests/src/android/security/keystore/recovery/KeyChainSnapshotTest.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore.recovery;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.google.common.collect.Lists;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+// TODO(b/73862682): Add tests for RecoveryCertPath
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class KeyChainSnapshotTest {
+
+    private static final int COUNTER_ID = 42;
+    private static final int SNAPSHOT_VERSION = 13;
+    private static final byte[] SALT = new byte[] { 0, 1, 0, 1 };
+    private static final byte[] SERVER_PARAMS = new byte[] { 6, 7, 9, 2 };
+    private static final byte[] RECOVERY_KEY_BLOB = new byte[] { 9, 1, 4, 6, 7 };
+    private static final int MAX_ATTEMPTS = 10;
+    private static final int LOCK_SCREEN_UI_FORMAT = KeyChainProtectionParams.UI_FORMAT_PASSWORD;
+    private static final int USER_SECRET_TYPE = KeyChainProtectionParams.TYPE_LOCKSCREEN;
+    private static final String KEY_ALIAS = "steph";
+    private static final byte[] KEY_MATERIAL = new byte[] { 3, 5, 7, 9, 1 };
+
+    @Test
+    public void build_setsCounterId() {
+        assertEquals(COUNTER_ID, createKeyChainSnapshot().getCounterId());
+    }
+
+    @Test
+    public void build_setsSnapshotVersion() {
+        assertEquals(SNAPSHOT_VERSION, createKeyChainSnapshot().getSnapshotVersion());
+    }
+
+    @Test
+    public void build_setsMaxAttempts() {
+        assertEquals(MAX_ATTEMPTS, createKeyChainSnapshot().getMaxAttempts());
+    }
+
+    @Test
+    public void build_setsServerParams() {
+        assertArrayEquals(SERVER_PARAMS, createKeyChainSnapshot().getServerParams());
+    }
+
+    @Test
+    public void build_setsRecoveryKeyBlob() {
+        assertArrayEquals(RECOVERY_KEY_BLOB,
+                createKeyChainSnapshot().getEncryptedRecoveryKeyBlob());
+    }
+
+    @Test
+    public void build_setsKeyChainProtectionParams() {
+        KeyChainSnapshot snapshot = createKeyChainSnapshot();
+
+        assertEquals(1, snapshot.getKeyChainProtectionParams().size());
+        KeyChainProtectionParams keyChainProtectionParams =
+                snapshot.getKeyChainProtectionParams().get(0);
+        assertEquals(USER_SECRET_TYPE, keyChainProtectionParams.getUserSecretType());
+        assertEquals(LOCK_SCREEN_UI_FORMAT, keyChainProtectionParams.getLockScreenUiFormat());
+        KeyDerivationParams keyDerivationParams = keyChainProtectionParams.getKeyDerivationParams();
+        assertEquals(KeyDerivationParams.ALGORITHM_SHA256, keyDerivationParams.getAlgorithm());
+        assertArrayEquals(SALT, keyDerivationParams.getSalt());
+    }
+
+    @Test
+    public void build_setsWrappedApplicationKeys() {
+        KeyChainSnapshot snapshot = createKeyChainSnapshot();
+
+        assertEquals(1, snapshot.getWrappedApplicationKeys().size());
+        WrappedApplicationKey wrappedApplicationKey = snapshot.getWrappedApplicationKeys().get(0);
+        assertEquals(KEY_ALIAS, wrappedApplicationKey.getAlias());
+        assertArrayEquals(KEY_MATERIAL, wrappedApplicationKey.getEncryptedKeyMaterial());
+    }
+
+    @Test
+    public void writeToParcel_writesCounterId() {
+        KeyChainSnapshot snapshot = writeToThenReadFromParcel(createKeyChainSnapshot());
+
+        assertEquals(COUNTER_ID, snapshot.getCounterId());
+    }
+
+    @Test
+    public void writeToParcel_writesSnapshotVersion() {
+        KeyChainSnapshot snapshot = writeToThenReadFromParcel(createKeyChainSnapshot());
+
+        assertEquals(SNAPSHOT_VERSION, snapshot.getSnapshotVersion());
+    }
+
+    @Test
+    public void writeToParcel_writesMaxAttempts() {
+        KeyChainSnapshot snapshot = writeToThenReadFromParcel(createKeyChainSnapshot());
+
+        assertEquals(MAX_ATTEMPTS, snapshot.getMaxAttempts());
+    }
+
+    @Test
+    public void writeToParcel_writesServerParams() {
+        KeyChainSnapshot snapshot = writeToThenReadFromParcel(createKeyChainSnapshot());
+
+        assertArrayEquals(SERVER_PARAMS, snapshot.getServerParams());
+    }
+
+    @Test
+    public void writeToParcel_writesKeyRecoveryBlob() {
+        KeyChainSnapshot snapshot = writeToThenReadFromParcel(createKeyChainSnapshot());
+
+        assertArrayEquals(RECOVERY_KEY_BLOB, snapshot.getEncryptedRecoveryKeyBlob());
+    }
+
+    @Test
+    public void writeToParcel_writesKeyChainProtectionParams() {
+        KeyChainSnapshot snapshot = writeToThenReadFromParcel(createKeyChainSnapshot());
+
+        assertEquals(1, snapshot.getKeyChainProtectionParams().size());
+        KeyChainProtectionParams keyChainProtectionParams =
+                snapshot.getKeyChainProtectionParams().get(0);
+        assertEquals(USER_SECRET_TYPE, keyChainProtectionParams.getUserSecretType());
+        assertEquals(LOCK_SCREEN_UI_FORMAT, keyChainProtectionParams.getLockScreenUiFormat());
+        KeyDerivationParams keyDerivationParams = keyChainProtectionParams.getKeyDerivationParams();
+        assertEquals(KeyDerivationParams.ALGORITHM_SHA256, keyDerivationParams.getAlgorithm());
+        assertArrayEquals(SALT, keyDerivationParams.getSalt());
+    }
+
+    @Test
+    public void writeToParcel_writesWrappedApplicationKeys() {
+        KeyChainSnapshot snapshot = writeToThenReadFromParcel(createKeyChainSnapshot());
+
+        assertEquals(1, snapshot.getWrappedApplicationKeys().size());
+        WrappedApplicationKey wrappedApplicationKey = snapshot.getWrappedApplicationKeys().get(0);
+        assertEquals(KEY_ALIAS, wrappedApplicationKey.getAlias());
+        assertArrayEquals(KEY_MATERIAL, wrappedApplicationKey.getEncryptedKeyMaterial());
+    }
+
+    private static KeyChainSnapshot createKeyChainSnapshot() {
+        return new KeyChainSnapshot.Builder()
+                .setCounterId(COUNTER_ID)
+                .setSnapshotVersion(SNAPSHOT_VERSION)
+                .setServerParams(SERVER_PARAMS)
+                .setMaxAttempts(MAX_ATTEMPTS)
+                .setEncryptedRecoveryKeyBlob(RECOVERY_KEY_BLOB)
+                .setKeyChainProtectionParams(Lists.newArrayList(createKeyChainProtectionParams()))
+                .setWrappedApplicationKeys(Lists.newArrayList(createWrappedApplicationKey()))
+                .build();
+    }
+
+    private static KeyChainProtectionParams createKeyChainProtectionParams() {
+        return new KeyChainProtectionParams.Builder()
+                .setKeyDerivationParams(createKeyDerivationParams())
+                .setUserSecretType(USER_SECRET_TYPE)
+                .setLockScreenUiFormat(LOCK_SCREEN_UI_FORMAT)
+                .build();
+    }
+
+    private static KeyDerivationParams createKeyDerivationParams() {
+        return KeyDerivationParams.createSha256Params(SALT);
+    }
+
+    private static WrappedApplicationKey createWrappedApplicationKey() {
+        return new WrappedApplicationKey.Builder()
+                .setAlias(KEY_ALIAS)
+                .setEncryptedKeyMaterial(KEY_MATERIAL)
+                .build();
+    }
+
+    private static KeyChainSnapshot writeToThenReadFromParcel(KeyChainSnapshot params) {
+        Parcel parcel = Parcel.obtain();
+        params.writeToParcel(parcel, /*flags=*/ 0);
+        parcel.setDataPosition(0);
+        KeyChainSnapshot fromParcel = KeyChainSnapshot.CREATOR.createFromParcel(parcel);
+        parcel.recycle();
+        return fromParcel;
+    }
+}
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 9a9cdbd..eee830f 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -19,6 +19,7 @@
 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.NetworkCapabilities.TRANSPORT_CELLULAR;
 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;
@@ -103,6 +104,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.net.Inet4Address;
+import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -1359,19 +1361,16 @@
 
         protected void setUpstreamNetwork(NetworkState ns) {
             String iface = null;
-            if (ns != null && ns.linkProperties != 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("Finding IPv4 upstream interface on: " + ns.linkProperties);
-                RouteInfo ipv4Default = RouteInfo.selectBestRoute(
-                    ns.linkProperties.getAllRoutes(), Inet4Address.ANY);
-                if (ipv4Default != null) {
-                    iface = ipv4Default.getInterface();
-                    mLog.i("Found interface " + ipv4Default.getInterface());
-                } else {
-                    mLog.i("No IPv4 upstream interface, giving up.");
-                }
+                mLog.i("Looking for default routes on: " + ns.linkProperties);
+                final String iface4 = getIPv4DefaultRouteInterface(ns);
+                final String iface6 = getIPv6DefaultRouteInterface(ns);
+                mLog.i("IPv4/IPv6 upstream interface(s): " + iface4 + "/" + iface6);
+
+                iface = (iface4 != null) ? iface4 : null /* TODO: iface6 */;
             }
 
             if (iface != null) {
@@ -2014,6 +2013,31 @@
         mTetherStates.remove(iface);
     }
 
+    private static String getIPv4DefaultRouteInterface(NetworkState ns) {
+        if (ns == null) return null;
+        return getInterfaceForDestination(ns.linkProperties, Inet4Address.ANY);
+    }
+
+    private static String getIPv6DefaultRouteInterface(NetworkState ns) {
+        if (ns == null) return null;
+        // An upstream network's IPv6 capability is currently only useful if it
+        // can be 64share'd downstream (RFC 7278). For now, that means mobile
+        // upstream networks only.
+        if (ns.networkCapabilities == null ||
+                !ns.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
+            return null;
+        }
+
+        return getInterfaceForDestination(ns.linkProperties, Inet6Address.ANY);
+    }
+
+    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;
+    }
+
     private static String[] copy(String[] strarray) {
         return Arrays.copyOf(strarray, strarray.length);
     }