Merge "Stop supporting legacy ConnectivityManager routing methods in M." into mnc-dev
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 01a1c18..3d264c6 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -220,11 +220,12 @@
         SYSTEM_SERVICE_NAMES.put(android.text.ClipboardManager.class, Context.CLIPBOARD_SERVICE);
 
         registerService(Context.CONNECTIVITY_SERVICE, ConnectivityManager.class,
-                new StaticServiceFetcher<ConnectivityManager>() {
+                new StaticOuterContextServiceFetcher<ConnectivityManager>() {
             @Override
-            public ConnectivityManager createService() {
+            public ConnectivityManager createService(Context context) {
                 IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
-                return new ConnectivityManager(IConnectivityManager.Stub.asInterface(b));
+                IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
+                return new ConnectivityManager(context, service);
             }});
 
         registerService(Context.COUNTRY_DETECTOR, CountryDetector.class,
@@ -793,4 +794,30 @@
 
         public abstract T createService();
     }
+
+    /**
+     * Like StaticServiceFetcher, creates only one instance of the service per process, but when
+     * creating the service for the first time, passes it the outer context of the creating
+     * component.
+     *
+     * TODO: Is this safe in the case where multiple applications share the same process?
+     * TODO: Delete this once its only user (ConnectivityManager) is known to work well in the
+     * case where multiple application components each have their own ConnectivityManager object.
+     */
+    static abstract class StaticOuterContextServiceFetcher<T> implements ServiceFetcher<T> {
+        private T mCachedInstance;
+
+        @Override
+        public final T getService(ContextImpl ctx) {
+            synchronized (StaticOuterContextServiceFetcher.this) {
+                if (mCachedInstance == null) {
+                    mCachedInstance = createService(ctx.getOuterContext());
+                }
+                return mCachedInstance;
+            }
+        }
+
+        public abstract T createService(Context applicationContext);
+    }
+
 }
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index ba63969..545e2fe 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -22,6 +22,7 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.net.NetworkUtils;
 import android.os.Binder;
 import android.os.Build.VERSION_CODES;
@@ -33,6 +34,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.Messenger;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.provider.Settings;
@@ -490,6 +492,8 @@
      */
     private static ConnectivityManager sInstance;
 
+    private final Context mContext;
+
     private INetworkManagementService mNMService;
 
     /**
@@ -892,8 +896,11 @@
      *
      * @deprecated Deprecated in favor of the cleaner
      *             {@link #requestNetwork(NetworkRequest, NetworkCallback)} API.
+     *             In {@link VERSION_CODES#MNC}, and above, this method is unsupported and will
+     *             throw {@code UnsupportedOperationException} if called.
      */
     public int startUsingNetworkFeature(int networkType, String feature) {
+        checkLegacyRoutingApiAccess();
         NetworkCapabilities netCap = networkCapabilitiesForFeature(networkType, feature);
         if (netCap == null) {
             Log.d(TAG, "Can't satisfy startUsingNetworkFeature for " + networkType + ", " +
@@ -939,8 +946,11 @@
      * always indicates failure.
      *
      * @deprecated Deprecated in favor of the cleaner {@link #unregisterNetworkCallback} API.
+     *             In {@link VERSION_CODES#MNC}, and above, this method is unsupported and will
+     *             throw {@code UnsupportedOperationException} if called.
      */
     public int stopUsingNetworkFeature(int networkType, String feature) {
+        checkLegacyRoutingApiAccess();
         NetworkCapabilities netCap = networkCapabilitiesForFeature(networkType, feature);
         if (netCap == null) {
             Log.d(TAG, "Can't satisfy stopUsingNetworkFeature for " + networkType + ", " +
@@ -1218,6 +1228,8 @@
      * @deprecated Deprecated in favor of the
      *             {@link #requestNetwork(NetworkRequest, NetworkCallback)},
      *             {@link #bindProcessToNetwork} and {@link Network#getSocketFactory} API.
+     *             In {@link VERSION_CODES#MNC}, and above, this method is unsupported and will
+     *             throw {@code UnsupportedOperationException} if called.
      */
     public boolean requestRouteToHost(int networkType, int hostAddress) {
         return requestRouteToHostAddress(networkType, NetworkUtils.intToInetAddress(hostAddress));
@@ -1238,6 +1250,7 @@
      *             {@link #bindProcessToNetwork} API.
      */
     public boolean requestRouteToHostAddress(int networkType, InetAddress hostAddress) {
+        checkLegacyRoutingApiAccess();
         try {
             return mService.requestRouteToHostAddress(networkType, hostAddress.getAddress());
         } catch (RemoteException e) {
@@ -1416,7 +1429,8 @@
     /**
      * {@hide}
      */
-    public ConnectivityManager(IConnectivityManager service) {
+    public ConnectivityManager(Context context, IConnectivityManager service) {
+        mContext = checkNotNull(context, "missing context");
         mService = checkNotNull(service, "missing IConnectivityManager");
         sInstance = this;
     }
@@ -2703,6 +2717,34 @@
         return new Network(netId);
     }
 
+    private void unsupportedStartingFrom(int version) {
+        if (Process.myUid() == Process.SYSTEM_UID) {
+            // The getApplicationInfo() call we make below is not supported in system context, and
+            // we want to allow the system to use these APIs anyway.
+            return;
+        }
+
+        if (mContext.getApplicationInfo().targetSdkVersion >= version) {
+            throw new UnsupportedOperationException(
+                    "This method is not supported in target SDK version " + version + " and above");
+        }
+    }
+
+    // Checks whether the calling app can use the legacy routing API (startUsingNetworkFeature,
+    // stopUsingNetworkFeature, requestRouteToHost), and if not throw UnsupportedOperationException.
+    // TODO: convert the existing system users (Tethering, GpsLocationProvider) to the new APIs and
+    // remove these exemptions. Note that this check is not secure, and apps can still access these
+    // functions by accessing ConnectivityService directly. However, it should be clear that doing
+    // so is unsupported and may break in the future. http://b/22728205
+    private void checkLegacyRoutingApiAccess() {
+        if (mContext.checkCallingOrSelfPermission("com.android.permission.INJECT_OMADM_SETTINGS")
+                == PackageManager.PERMISSION_GRANTED) {
+            return;
+        }
+
+        unsupportedStartingFrom(VERSION_CODES.MNC);
+    }
+
     /**
      * Binds host resolutions performed by this process to {@code network}.
      * {@link #bindProcessToNetwork} takes precedence over this setting.
diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
index 3618e1a..19d29f3 100644
--- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
@@ -463,7 +463,7 @@
         mService = new WrappedConnectivityService(
                 mServiceContext, mNetManager, mStatsService, mPolicyService);
         mService.systemReady();
-        mCm = new ConnectivityManager(mService);
+        mCm = new ConnectivityManager(getContext(), mService);
     }
 
     private int transportToLegacyType(int transport) {