Merge "Zygote: Allow arbitrary-length wrap property names"
diff --git a/Android.mk b/Android.mk
index 02caa0a..2571afb 100644
--- a/Android.mk
+++ b/Android.mk
@@ -553,7 +553,6 @@
         frameworks/base/telephony/java/android/telephony/mbms/FileInfo.aidl \
         frameworks/base/telephony/java/android/telephony/mbms/FileServiceInfo.aidl \
         frameworks/base/telephony/java/android/telephony/mbms/ServiceInfo.aidl \
-	frameworks/base/telephony/java/android/telephony/mbms/StreamingService.aidl \
         frameworks/base/telephony/java/android/telephony/mbms/StreamingServiceInfo.aidl \
 	frameworks/base/telephony/java/android/telephony/ServiceState.aidl \
 	frameworks/base/telephony/java/android/telephony/SubscriptionInfo.aidl \
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index 3135b30..b12ff72 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -16,7 +16,6 @@
 
 package android.bluetooth;
 
-import android.content.Context;
 import android.os.Handler;
 import android.os.ParcelUuid;
 import android.os.RemoteException;
@@ -927,6 +926,31 @@
     }
 
     /**
+     * Discovers a service by UUID. This is exposed only for passing PTS tests.
+     * It should never be used by real applications. The service is not searched
+     * for characteristics and descriptors, or returned in any callback.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+     *
+     * @return true, if the remote service discovery has been started
+     * @hide
+     */
+    public boolean discoverServiceByUuid(UUID uuid) {
+        if (DBG) Log.d(TAG, "discoverServiceByUuid() - device: " + mDevice.getAddress());
+        if (mService == null || mClientIf == 0) return false;
+
+        mServices.clear();
+
+        try {
+            mService.discoverServiceByUuid(mClientIf, mDevice.getAddress(), new ParcelUuid(uuid));
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+            return false;
+        }
+        return true;
+    }
+
+    /**
      * Returns a list of GATT services offered by the remote device.
      *
      * <p>This function requires that service discovery has been completed
diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl
index 63bd942..4ff5976 100644
--- a/core/java/android/bluetooth/IBluetoothGatt.aidl
+++ b/core/java/android/bluetooth/IBluetoothGatt.aidl
@@ -76,6 +76,7 @@
     void clientReadPhy(in int clientIf, in String address);
     void refreshDevice(in int clientIf, in String address);
     void discoverServices(in int clientIf, in String address);
+    void discoverServiceByUuid(in int clientIf, in String address, in ParcelUuid uuid);
     void readCharacteristic(in int clientIf, in String address, in int handle, in int authReq);
     void readUsingCharacteristicUuid(in int clientIf, in String address, in ParcelUuid uuid,
                            in int startHandle, in int endHandle, in int authReq);
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 6320134..234bef7 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -44,7 +44,6 @@
 import android.telephony.SubscriptionManager;
 import android.util.ArrayMap;
 import android.util.Log;
-import android.util.SparseArray;
 import android.util.SparseIntArray;
 
 import com.android.internal.telephony.ITelephony;
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 6000fb5..d73e7dd 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -27,6 +27,7 @@
 #include <fcntl.h>
 #include <grp.h>
 #include <inttypes.h>
+#include <malloc.h>
 #include <mntent.h>
 #include <paths.h>
 #include <signal.h>
@@ -519,6 +520,9 @@
     // The child process.
     gMallocLeakZygoteChild = 1;
 
+    // Set the jemalloc decay time to 1.
+    mallopt(M_DECAY_TIME, 1);
+
     // Clean up any descriptors which must be closed immediately
     DetachDescriptors(env, fdsToClose);
 
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
index d38beb3..44c61f0 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
@@ -16,6 +16,7 @@
 
 package com.android.server.connectivity.tethering;
 
+import static android.content.Context.TELEPHONY_SERVICE;
 import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
 import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
@@ -47,9 +48,9 @@
 public class TetheringConfiguration {
     private static final String TAG = TetheringConfiguration.class.getSimpleName();
 
-    private static final int DUN_NOT_REQUIRED = 0;
-    private static final int DUN_REQUIRED = 1;
-    private static final int DUN_UNSPECIFIED = 2;
+    public static final int DUN_NOT_REQUIRED = 0;
+    public static final int DUN_REQUIRED = 1;
+    public 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
@@ -81,8 +82,9 @@
         tetherableBluetoothRegexs = ctx.getResources().getStringArray(
                 com.android.internal.R.array.config_tether_bluetooth_regexs);
 
-        isDunRequired = checkDunRequired(ctx);
-        preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(ctx, isDunRequired);
+        final int dunCheck = checkDunRequired(ctx);
+        preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(ctx, dunCheck);
+        isDunRequired = preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN);
 
         dhcpRanges = getDhcpRanges(ctx);
         defaultIPv4DNS = copy(DEFAULT_IPV4_DNS);
@@ -138,14 +140,12 @@
         pw.println();
     }
 
-    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 int checkDunRequired(Context ctx) {
+        final TelephonyManager tm = (TelephonyManager) ctx.getSystemService(TELEPHONY_SERVICE);
+        return (tm != null) ? tm.getTetherApnRequired() : DUN_UNSPECIFIED;
     }
 
-    private static Collection<Integer> getUpstreamIfaceTypes(Context ctx, boolean requiresDun) {
+    private static Collection<Integer> getUpstreamIfaceTypes(Context ctx, int dunCheck) {
         final int ifaceTypes[] = ctx.getResources().getIntArray(
                 com.android.internal.R.array.config_tether_upstream_types);
         final ArrayList<Integer> upstreamIfaceTypes = new ArrayList<>(ifaceTypes.length);
@@ -153,10 +153,10 @@
             switch (i) {
                 case TYPE_MOBILE:
                 case TYPE_MOBILE_HIPRI:
-                    if (requiresDun) continue;
+                    if (dunCheck == DUN_REQUIRED) continue;
                     break;
                 case TYPE_MOBILE_DUN:
-                    if (!requiresDun) continue;
+                    if (dunCheck == DUN_NOT_REQUIRED) continue;
                     break;
             }
             upstreamIfaceTypes.add(i);
@@ -166,7 +166,7 @@
         // of the value of |requiresDun|, cell data of one form or another is
         // *always* an upstream, regardless of the upstream interface types
         // specified by configuration resources.
-        if (requiresDun) {
+        if (dunCheck == DUN_REQUIRED) {
             if (!upstreamIfaceTypes.contains(TYPE_MOBILE_DUN)) {
                 upstreamIfaceTypes.add(TYPE_MOBILE_DUN);
             }
diff --git a/telephony/java/android/telephony/MbmsStreamingManager.java b/telephony/java/android/telephony/MbmsStreamingManager.java
index 58262e1..251d5bb 100644
--- a/telephony/java/android/telephony/MbmsStreamingManager.java
+++ b/telephony/java/android/telephony/MbmsStreamingManager.java
@@ -24,10 +24,10 @@
 import android.content.pm.ResolveInfo;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.telephony.mbms.IMbmsStreamingManagerCallback;
-import android.telephony.mbms.IStreamingServiceCallback;
 import android.telephony.mbms.MbmsException;
+import android.telephony.mbms.MbmsStreamingManagerCallback;
 import android.telephony.mbms.StreamingService;
+import android.telephony.mbms.StreamingServiceCallback;
 import android.telephony.mbms.StreamingServiceInfo;
 import android.telephony.mbms.vendor.IMbmsStreamingService;
 import android.util.Log;
@@ -77,14 +77,14 @@
     };
     private List<ServiceListener> mServiceListeners = new LinkedList<>();
 
-    private IMbmsStreamingManagerCallback mCallbackToApp;
+    private MbmsStreamingManagerCallback mCallbackToApp;
     private final String mAppName;
 
     private final Context mContext;
     private int mSubscriptionId = INVALID_SUBSCRIPTION_ID;
 
     /** @hide */
-    private MbmsStreamingManager(Context context, IMbmsStreamingManagerCallback listener,
+    private MbmsStreamingManager(Context context, MbmsStreamingManagerCallback listener,
                     String streamingAppName, int subscriptionId) {
         mContext = context;
         mAppName = streamingAppName;
@@ -106,7 +106,7 @@
      * @param subscriptionId The subscription ID to use.
      */
     public static MbmsStreamingManager create(Context context,
-            IMbmsStreamingManagerCallback listener, String streamingAppName, int subscriptionId)
+            MbmsStreamingManagerCallback listener, String streamingAppName, int subscriptionId)
             throws MbmsException {
         MbmsStreamingManager manager = new MbmsStreamingManager(context, listener,
                 streamingAppName, subscriptionId);
@@ -116,10 +116,10 @@
 
     /**
      * Create a new MbmsStreamingManager using the system default data subscription ID.
-     * See {@link #create(Context, IMbmsStreamingManagerCallback, String, int)}.
+     * See {@link #create(Context, MbmsStreamingManagerCallback, String, int)}.
      */
     public static MbmsStreamingManager create(Context context,
-            IMbmsStreamingManagerCallback listener, String streamingAppName)
+            MbmsStreamingManagerCallback listener, String streamingAppName)
             throws MbmsException {
         int subId = SubscriptionManager.getDefaultSubscriptionId();
         MbmsStreamingManager manager = new MbmsStreamingManager(context, listener,
@@ -155,13 +155,12 @@
      *
      * This may throw an {@link MbmsException} containing one of the following errors:
      * {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}
-     * {@link MbmsException#ERROR_NOT_YET_INITIALIZED}
+     * {@link MbmsException#ERROR_UNKNOWN_REMOTE_EXCEPTION}
      * {@link MbmsException#ERROR_CONCURRENT_SERVICE_LIMIT_REACHED}
      *
-     * Asynchronous error codes via the {@link IMbmsStreamingManagerCallback#error(int, String)}
+     * Asynchronous error codes via the {@link MbmsStreamingManagerCallback#error(int, String)}
      * callback can include any of the errors except:
      * {@link MbmsException#ERROR_UNABLE_TO_START_SERVICE}
-     * {@link MbmsException#ERROR_INVALID_SERVICE_ID}
      * {@link MbmsException#ERROR_END_OF_SESSION}
      */
     public void getStreamingServices(List<String> classList) throws MbmsException {
@@ -180,36 +179,37 @@
 
     /**
      * Starts streaming a requested service, reporting status to the indicated listener.
-     * Returns an object used to control that stream.
+     * Returns an object used to control that stream. The stream may not be ready for consumption
+     * immediately upon return from this method -- wait until the streaming state has been
+     * reported via {@link android.telephony.mbms.StreamingServiceCallback#streamStateChanged(int)}.
      *
-     * May throw an IllegalArgumentException or RemoteException.
+     * May throw an {@link MbmsException} containing any of the following error codes:
+     * {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}
+     * {@link MbmsException#ERROR_UNKNOWN_REMOTE_EXCEPTION}
+     * {@link MbmsException#ERROR_CONCURRENT_SERVICE_LIMIT_REACHED}
+     *
+     * May also throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
      *
      * Asynchronous errors through the listener include any of the errors
      */
     public StreamingService startStreaming(StreamingServiceInfo serviceInfo,
-            IStreamingServiceCallback listener) {
-        return null;
-    }
+            StreamingServiceCallback listener) throws MbmsException {
+        if (mService == null) {
+            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
+        }
 
-    /**
-     * Lists all the services currently being streamed to the device by this application
-     * on this given subId.  Results are returned asynchronously through the previously
-     * registered callback.
-     *
-     * May throw a RemoteException.
-     *
-     * The return value is a success/error-code with the following possible values:
-     * <li>SUCCESS</li>
-     * <li>ERROR_MSDC_CONCURRENT_SERVICE_LIMIT_REACHED</li>
-     *
-     * Asynchronous errors through the listener include any of the errors except
-     * <li>ERROR_UNABLED_TO_START_SERVICE</li>
-     * <li>ERROR_MSDC_INVALID_SERVICE_ID</li>
-     * <li>ERROR_MSDC_END_OF_SESSION</li>
-     *
-     */
-    public int getActiveStreamingServices() {
-        return 0;
+        try {
+            int returnCode = mService.startStreaming(
+                    mAppName, mSubscriptionId, serviceInfo.getServiceId(), listener);
+            if (returnCode != MbmsException.SUCCESS) {
+                throw new MbmsException(returnCode);
+            }
+        } catch (RemoteException e) {
+            throw new MbmsException(MbmsException.ERROR_UNKNOWN_REMOTE_EXCEPTION);
+        }
+
+        return new StreamingService(
+                mAppName, mSubscriptionId, mService, serviceInfo, listener);
     }
 
     private void bindAndInitialize() throws MbmsException {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index ee25839..3fcb280 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -5728,35 +5728,75 @@
     }
 
     /**
-     * Set SIM card power state. Request is equivalent to inserting or removing the card.
+     * Requested state of SIM
      *
-     * @param powerUp True if powering up the SIM, otherwise powering down
+     * CARD_POWER_DOWN
+     * Powers down the SIM. SIM must be up prior.
+     *
+     * CARD_POWER_UP
+     * Powers up the SIM normally. SIM must be down prior.
+     *
+     * CARD_POWER_UP_PASS_THROUGH
+     * Powers up the SIM in PASS_THROUGH mode. SIM must be down prior.
+     * When SIM is powered up in PASS_THOUGH mode, the modem does not send
+     * any command to it (for example SELECT of MF, or TERMINAL CAPABILITY),
+     * and the SIM card is controlled completely by Telephony sending APDUs
+     * directly. The SIM card state will be RIL_CARDSTATE_PRESENT and the
+     * number of card apps will be 0.
+     * No new error code is generated. Emergency calls are supported in the
+     * same way as if the SIM card is absent.
+     * The PASS_THROUGH mode is valid only for the specific card session where it
+     * is activated, and normal behavior occurs at the next SIM initialization,
+     * unless PASS_THROUGH mode is requested again. Hence, the last power-up mode
+     * is NOT persistent across boots. On reboot, SIM will power up normally.
+     */
+    /** @hide */
+    public static final int CARD_POWER_DOWN = 0;
+    /** @hide */
+    public static final int CARD_POWER_UP = 1;
+    /** @hide */
+    public static final int CARD_POWER_UP_PASS_THROUGH = 2;
+
+    /**
+     * Set SIM card power state.
+     *
+     * @param state  State of SIM (power down, power up, pass through)
+     * @see #CARD_POWER_DOWN
+     * @see #CARD_POWER_UP
+     * @see #CARD_POWER_UP_PASS_THROUGH
+     * Callers should monitor for {@link TelephonyIntents#ACTION_SIM_STATE_CHANGED}
+     * broadcasts to determine success or failure and timeout if needed.
      *
      * <p>Requires Permission:
      *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
      *
      * @hide
      **/
-    public void setSimPowerState(boolean powerUp) {
-        setSimPowerStateForSlot(getSlotIndex(), powerUp);
+    public void setSimPowerState(int state) {
+        setSimPowerStateForSlot(getSlotIndex(), state);
     }
 
     /**
-     * Set SIM card power state. Request is equivalent to inserting or removing the card.
+     * Set SIM card power state.
      *
      * @param slotIndex SIM slot id
-     * @param powerUp True if powering up the SIM, otherwise powering down
+     * @param state  State of SIM (power down, power up, pass through)
+     * @see #CARD_POWER_DOWN
+     * @see #CARD_POWER_UP
+     * @see #CARD_POWER_UP_PASS_THROUGH
+     * Callers should monitor for {@link TelephonyIntents#ACTION_SIM_STATE_CHANGED}
+     * broadcasts to determine success or failure and timeout if needed.
      *
      * <p>Requires Permission:
      *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
      *
      * @hide
      **/
-    public void setSimPowerStateForSlot(int slotIndex, boolean powerUp) {
+    public void setSimPowerStateForSlot(int slotIndex, int state) {
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                telephony.setSimPowerStateForSlot(slotIndex, powerUp);
+                telephony.setSimPowerStateForSlot(slotIndex, state);
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling ITelephony#setSimPowerStateForSlot", e);
diff --git a/telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl b/telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl
index 6d2e031..891edad 100755
--- a/telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl
+++ b/telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl
@@ -17,14 +17,13 @@
 package android.telephony.mbms;
 
 import android.net.Uri;
-import android.telephony.mbms.StreamingService;
 
 /**
  * @hide
  */
 oneway interface IStreamingServiceCallback {
     void error(int errorCode, String message);
-    void streamStateChanged(in StreamingService service, int state);
+    void streamStateChanged(int state);
     void uriUpdated(in Uri uri);
     void broadcastSignalStrengthUpdated(int signalStrength);
 }
diff --git a/telephony/java/android/telephony/mbms/MbmsException.java b/telephony/java/android/telephony/mbms/MbmsException.java
index e8680ea..a0ded9b 100644
--- a/telephony/java/android/telephony/mbms/MbmsException.java
+++ b/telephony/java/android/telephony/mbms/MbmsException.java
@@ -27,16 +27,14 @@
     public static final int ERROR_CONCURRENT_SERVICE_LIMIT_REACHED = 6;
     public static final int ERROR_MIDDLEWARE_NOT_BOUND = 7;
     public static final int ERROR_UNABLE_TO_START_SERVICE = 8;
-    public static final int ERROR_INVALID_SERVICE_ID = 9;
+    public static final int ERROR_STREAM_ALREADY_STARTED = 9;
     public static final int ERROR_END_OF_SESSION = 10;
-    public static final int ERROR_NOT_YET_INITIALIZED = 11;
-    public static final int ERROR_APP_PERMISSIONS_NOT_GRANTED = 12;
 
     private final int mErrorCode;
 
     /** @hide
      * TODO: future systemapi
-     * */
+     */
     public MbmsException(int errorCode) {
         super();
         mErrorCode = errorCode;
diff --git a/telephony/java/android/telephony/mbms/StreamingService.aidl b/telephony/java/android/telephony/mbms/StreamingService.aidl
deleted file mode 100755
index 0c286f3..0000000
--- a/telephony/java/android/telephony/mbms/StreamingService.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
-** Copyright 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.telephony.mbms;
-
-parcelable StreamingService;
diff --git a/telephony/java/android/telephony/mbms/StreamingService.java b/telephony/java/android/telephony/mbms/StreamingService.java
index 08c4a27..ac5fbdf 100644
--- a/telephony/java/android/telephony/mbms/StreamingService.java
+++ b/telephony/java/android/telephony/mbms/StreamingService.java
@@ -19,45 +19,60 @@
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.RemoteException;
+import android.telephony.mbms.vendor.IMbmsStreamingService;
+import android.util.Log;
 
 /**
  * @hide
  */
 public class StreamingService {
-
+    private static final String LOG_TAG = "MbmsStreamingService";
     public final static int STATE_STOPPED = 1;
     public final static int STATE_STARTED = 2;
     public final static int STATE_STALLED = 3;
 
+    private final String mAppName;
+    private final int mSubscriptionId;
+    private final IMbmsStreamingService mService;
+    private final StreamingServiceInfo mServiceInfo;
+    private final IStreamingServiceCallback mCallback;
     /**
+     * @hide
      */
-    StreamingService(StreamingServiceInfo streamingServiceInfo,
-            IStreamingServiceCallback listener) {
+    public StreamingService(String appName,
+            int subscriptionId,
+            IMbmsStreamingService service,
+            StreamingServiceInfo streamingServiceInfo,
+            IStreamingServiceCallback callback) {
+        mAppName = appName;
+        mSubscriptionId = subscriptionId;
+        mService = service;
+        mServiceInfo = streamingServiceInfo;
+        mCallback = callback;
     }
 
     /**
      * Retreive the Uri used to play this stream.
      *
-     * This may throw a RemoteException.
+     * This may throw a {@link MbmsException} with the error code
+     * {@link MbmsException#ERROR_UNKNOWN_REMOTE_EXCEPTION}
+     * @return The {@link Uri} to pass to the streaming client.
      */
-    public Uri getPlaybackUri() {
-        return null;
+    public Uri getPlaybackUri() throws MbmsException {
+        try {
+            return mService.getPlaybackUri(mAppName, mSubscriptionId, mServiceInfo.getServiceId());
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "Caught remote exception calling getPlaybackUri: " + e);
+            throw new MbmsException(MbmsException.ERROR_UNKNOWN_REMOTE_EXCEPTION);
+        }
     }
 
     /**
      * Retreive the info for this StreamingService.
      */
     public StreamingServiceInfo getInfo() {
-        return null;
-    }
-
-    /**
-     * Retreive the current state of this stream.
-     *
-     * This may throw a RemoteException.
-     */
-    public int getState() {
-        return STATE_STOPPED;
+        return mServiceInfo;
     }
 
     /**
@@ -68,30 +83,13 @@
     public void stopStreaming() {
     }
 
-    public void dispose() {
-    }
-
-    public static final Parcelable.Creator<StreamingService> CREATOR =
-            new Parcelable.Creator<StreamingService>() {
-        @Override
-        public StreamingService createFromParcel(Parcel in) {
-            return new StreamingService(in);
+    public void dispose() throws MbmsException {
+        try {
+            mService.disposeStream(mAppName, mSubscriptionId, mServiceInfo.getServiceId());
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "Caught remote exception calling disposeStream: " + e);
+            throw new MbmsException(MbmsException.ERROR_UNKNOWN_REMOTE_EXCEPTION);
         }
-
-        @Override
-        public StreamingService[] newArray(int size) {
-            return new StreamingService[size];
-        }
-    };
-
-    private StreamingService(Parcel in) {
-    }
-
-    public void writeToParcel(Parcel dest, int flags) {
-    }
-
-    public int describeContents() {
-        return 0;
     }
 }
 
diff --git a/telephony/java/android/telephony/mbms/StreamingServiceCallback.java b/telephony/java/android/telephony/mbms/StreamingServiceCallback.java
index 752a4b3..bd0a1b3 100644
--- a/telephony/java/android/telephony/mbms/StreamingServiceCallback.java
+++ b/telephony/java/android/telephony/mbms/StreamingServiceCallback.java
@@ -19,7 +19,7 @@
 import android.net.Uri;
 
 /**
- * A Callback class for use when the applicaiton is actively streaming content.
+ * A Callback class for use when the application is actively streaming content.
  * @hide
  */
 public class StreamingServiceCallback extends IStreamingServiceCallback.Stub {
@@ -33,7 +33,6 @@
      */
     public static final int SIGNAL_STRENGTH_UNAVAILABLE = -1;
 
-
     public void error(int errorCode, String message) {
         // default implementation empty
     }
@@ -44,7 +43,7 @@
      * See {@link StreamingService#STATE_STOPPED}, {@link StreamingService#STATE_STARTED}
      * and {@link StreamingService#STATE_STALLED}.
      */
-    public void streamStateChanged(StreamingService service, int state) {
+    public void streamStateChanged(int state) {
         // default implementation empty
     }
 
diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl
index a77a10a..8ff7fa7 100755
--- a/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl
+++ b/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl
@@ -19,7 +19,6 @@
 import android.net.Uri;
 import android.telephony.mbms.IMbmsStreamingManagerCallback;
 import android.telephony.mbms.IStreamingServiceCallback;
-import android.telephony.mbms.StreamingService;
 import android.telephony.mbms.StreamingServiceInfo;
 
 /**
@@ -32,35 +31,19 @@
 
     int getStreamingServices(String appName, int subId, in List<String> serviceClasses);
 
-    /**
-     * - Starts streaming the serviceId given.
-     * - if the uid/appName/subId don't match a previously registered callback an error will
-     *   be returned
-     * - Streaming status will be sent via the included listener, including an initial
-     *   URL-change and State-change pair.
-     */
     int startStreaming(String appName, int subId, String serviceId,
             IStreamingServiceCallback listener);
 
     /**
-     * Asynchronously fetches all Services being streamed by this uid/appName/subId.
-     */
-    int getActiveStreamingServices(String appName, int subId);
-
-
-    /**
      * Per-stream api.  Note each specifies what stream they apply to.
      */
 
     Uri getPlaybackUri(String appName, int subId, String serviceId);
 
-    int getState(String appName, int subId, String serviceId);
-
     void stopStreaming(String appName, int subId, String serviceId);
 
     void disposeStream(String appName, int subId, String serviceId);
 
-
     /**
      * End of life for all MbmsStreamingManager's created by this uid/appName/subId.
      * Ends any streams run under this uid/appname/subId and calls the disposed methods
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
index 4670352..37f47ff 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
@@ -16,6 +16,7 @@
 
 package android.telephony.mbms.vendor;
 
+import android.annotation.Nullable;
 import android.net.Uri;
 import android.os.RemoteException;
 import android.telephony.mbms.IMbmsStreamingManagerCallback;
@@ -32,11 +33,12 @@
     /**
      * Initialize streaming service for this app and subId, registering the listener.
      *
+     * May throw an {@link IllegalArgumentException} or a {@link SecurityException}
+     *
      * @param listener The callback to use to communicate with the app.
      * @param appName The app name as negotiated with the wireless carrier.
      * @param subscriptionId The subscription ID to use.
-     * @return {@link MbmsException#SUCCESS}, {@link MbmsException#ERROR_ALREADY_INITIALIZED}, or
-     *         {@link MbmsException#ERROR_APP_PERMISSIONS_NOT_GRANTED}
+     * @return {@link MbmsException#SUCCESS} or {@link MbmsException#ERROR_ALREADY_INITIALIZED}
      */
     @Override
     public int initialize(IMbmsStreamingManagerCallback listener, String appName,
@@ -52,6 +54,8 @@
      * Note that subsequent calls with the same uid, appName and subId will replace
      * the service class list.
      *
+     * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
+     *
      * @param appName The app name as negotiated with the wireless carrier.
      * @param subscriptionId The subscription id to use.
      * @param serviceClasses The service classes that the app wishes to get info on. The strings
@@ -59,7 +63,6 @@
      *                       carrier.
      * @return One of {@link MbmsException#SUCCESS},
      *         {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND},
-     *         {@link MbmsException#ERROR_NOT_YET_INITIALIZED}, or
      *         {@link MbmsException#ERROR_CONCURRENT_SERVICE_LIMIT_REACHED}
      */
     @Override
@@ -68,28 +71,43 @@
         return 0;
     }
 
+    /**
+     * Starts streaming on a particular service. This method may perform asynchronous work. When
+     * the middleware is ready to send bits to the frontend, it should inform the app via
+     * {@link IStreamingServiceCallback#streamStateChanged(int)}.
+     *
+     * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
+     *
+     * @param appName The app name as negotiated with the wireless carrier.
+     * @param subscriptionId The subscription id to use.
+     * @param serviceId The ID of the streaming service that the app has requested.
+     * @param listener The listener object on which the app wishes to receive updates.
+     * @return TODO: document possible errors
+     */
     @Override
-    public int startStreaming(String appName, int subId,
+    public int startStreaming(String appName, int subscriptionId,
             String serviceId, IStreamingServiceCallback listener) throws RemoteException {
         return 0;
     }
 
+    /**
+     * Retrieves the streaming URI for a particular service. If the middleware is not yet ready to
+     * stream the service, this method may return null.
+     *
+     * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
+     *
+     * @param appName The app name as negotiated with the wireless carrier.
+     * @param subscriptionId The subscription id to use.
+     * @param serviceId The ID of the streaming service that the app has requested.
+     * @return An opaque {@link Uri} to be passed to a video player that understands the format.
+     */
     @Override
-    public int getActiveStreamingServices(String appName, int subId) throws RemoteException {
-        return 0;
-    }
-
-    @Override
-    public Uri getPlaybackUri(String appName, int subId, String serviceId) throws RemoteException {
+    public @Nullable Uri getPlaybackUri(String appName, int subscriptionId, String serviceId)
+            throws RemoteException {
         return null;
     }
 
     @Override
-    public int getState(String appName, int subId, String serviceId) throws RemoteException {
-        return 0;
-    }
-
-    @Override
     public void stopStreaming(String appName, int subId, String serviceId) throws RemoteException {
     }
 
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 10e9035..c542879 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1291,12 +1291,12 @@
     List<ClientRequestStats> getClientRequestStats(String callingPackage, int subid);
 
     /**
-     * Set SIM card power state. Request is equivalent to inserting or removing the card.
+     * Set SIM card power state.
      * @param slotIndex SIM slot id
-     * @param powerUp True if powering up the SIM, otherwise powering down
+     * @param state  State of SIM (power down, power up, pass through)
      * @hide
      * */
-    void setSimPowerStateForSlot(int slotIndex, boolean powerUp);
+    void setSimPowerStateForSlot(int slotIndex, int state);
 
     /**
      * Returns a list of Forbidden PLMNs from the specified SIM App
diff --git a/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java b/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
new file mode 100644
index 0000000..9fcd1b5
--- /dev/null
+++ b/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
@@ -0,0 +1,132 @@
+/*
+ * 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 static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
+import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
+import static android.net.ConnectivityManager.TYPE_WIFI;
+import static com.android.server.connectivity.tethering.TetheringConfiguration.DUN_NOT_REQUIRED;
+import static com.android.server.connectivity.tethering.TetheringConfiguration.DUN_REQUIRED;
+import static com.android.server.connectivity.tethering.TetheringConfiguration.DUN_UNSPECIFIED;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.res.Resources;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.util.test.BroadcastInterceptingContext;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class TetheringConfigurationTest {
+    @Mock private Context mContext;
+    @Mock private TelephonyManager mTelephonyManager;
+    @Mock private Resources mResources;
+    private Context mMockContext;
+    private boolean mHasTelephonyManager;
+
+    private class MockContext extends BroadcastInterceptingContext {
+        MockContext(Context base) {
+            super(base);
+        }
+
+        @Override
+        public Resources getResources() { return mResources; }
+
+        @Override
+        public Object getSystemService(String name) {
+            if (Context.TELEPHONY_SERVICE.equals(name)) {
+                return mHasTelephonyManager ? mTelephonyManager : null;
+            }
+            return super.getSystemService(name);
+        }
+    }
+
+    @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[0]);
+        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_bluetooth_regexs))
+                .thenReturn(new String[0]);
+        mMockContext = new MockContext(mContext);
+    }
+
+    @Test
+    public void testDunFromTelephonyManagerMeansDun() {
+        when(mResources.getIntArray(com.android.internal.R.array.config_tether_upstream_types))
+                .thenReturn(new int[]{TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI});
+        mHasTelephonyManager = true;
+        when(mTelephonyManager.getTetherApnRequired()).thenReturn(DUN_REQUIRED);
+
+        final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext);
+        assertTrue(cfg.isDunRequired);
+        assertTrue(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN));
+        assertFalse(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE));
+        assertFalse(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_HIPRI));
+        // Just to prove we haven't clobbered Wi-Fi:
+        assertTrue(cfg.preferredUpstreamIfaceTypes.contains(TYPE_WIFI));
+    }
+
+    @Test
+    public void testDunNotRequiredFromTelephonyManagerMeansNoDun() {
+        when(mResources.getIntArray(com.android.internal.R.array.config_tether_upstream_types))
+                .thenReturn(new int[]{TYPE_MOBILE_DUN, TYPE_WIFI});
+        mHasTelephonyManager = true;
+        when(mTelephonyManager.getTetherApnRequired()).thenReturn(DUN_NOT_REQUIRED);
+
+        final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext);
+        assertFalse(cfg.isDunRequired);
+        assertFalse(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN));
+        assertTrue(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE));
+        assertTrue(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_HIPRI));
+        // Just to prove we haven't clobbered Wi-Fi:
+        assertTrue(cfg.preferredUpstreamIfaceTypes.contains(TYPE_WIFI));
+    }
+
+    @Test
+    public void testDunFromUpstreamConfigMeansDun() {
+        when(mResources.getIntArray(com.android.internal.R.array.config_tether_upstream_types))
+                .thenReturn(new int[]{TYPE_MOBILE_DUN, TYPE_WIFI});
+        mHasTelephonyManager = false;
+        when(mTelephonyManager.getTetherApnRequired()).thenReturn(DUN_UNSPECIFIED);
+
+        final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext);
+        assertTrue(cfg.isDunRequired);
+        assertTrue(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN));
+        // Just to prove we haven't clobbered Wi-Fi:
+        assertTrue(cfg.preferredUpstreamIfaceTypes.contains(TYPE_WIFI));
+    }
+}