CTS to test service binders accessibility from isolated processes

Isolated processes were able to access some service binders which they
should not have access to, since they were getting cached in
ServiceManager. This is to test the corresponding fix

Bug: 30202228
Change-Id: I125599f4e793b3f2675d2695f9f06afb9623f9b8
diff --git a/tests/tests/security/Android.mk b/tests/tests/security/Android.mk
index 1f7b601..f2b4470 100644
--- a/tests/tests/security/Android.mk
+++ b/tests/tests/security/Android.mk
@@ -55,7 +55,8 @@
 		libstagefright_foundation
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)\
-                   src/android/security/cts/activity/ISecureRandomService.aidl
+                   src/android/security/cts/activity/ISecureRandomService.aidl\
+                   aidl/android/security/cts/IIsolatedService.aidl
 
 LOCAL_PACKAGE_NAME := CtsSecurityTestCases
 
diff --git a/tests/tests/security/AndroidManifest.xml b/tests/tests/security/AndroidManifest.xml
index 7a67b08..7468d68 100644
--- a/tests/tests/security/AndroidManifest.xml
+++ b/tests/tests/security/AndroidManifest.xml
@@ -35,6 +35,10 @@
                  android:isolatedProcess="true"
                  android:exported="true"/>
 
+        <service android:name="android.security.cts.IsolatedService"
+                 android:process=":Isolated"
+                 android:isolatedProcess="true"/>
+
         <service android:name="android.security.cts.activity.SecureRandomService"
                  android:process=":secureRandom"/>
     </application>
diff --git a/tests/tests/security/aidl/android/security/cts/IIsolatedService.aidl b/tests/tests/security/aidl/android/security/cts/IIsolatedService.aidl
new file mode 100644
index 0000000..16db0fe
--- /dev/null
+++ b/tests/tests/security/aidl/android/security/cts/IIsolatedService.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+interface IIsolatedService {
+    String[] getCachedSystemServices();
+    IBinder getSystemService(String serviceName);
+}
diff --git a/tests/tests/security/src/android/security/cts/IsolatedProcessTest.java b/tests/tests/security/src/android/security/cts/IsolatedProcessTest.java
new file mode 100644
index 0000000..ac264d0
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/IsolatedProcessTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.security.cts;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.security.cts.IIsolatedService;
+import android.security.cts.IsolatedService;
+import android.test.AndroidTestCase;
+import android.util.Log;
+import com.android.internal.util.ArrayUtils;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import junit.framework.Assert;
+
+public class IsolatedProcessTest extends AndroidTestCase {
+    static final String TAG = IsolatedProcessTest.class.getSimpleName();
+
+    private static final long BIND_SERVICE_TIMEOUT = 5000;
+
+    // No service other than these should be visible to an isolated process
+    private static final String[] SERVICES_ALLOWED_TO_ISOLATED_PROCESS = {
+            "package",
+            Context.ACTIVITY_SERVICE
+    };
+    // Arbitrary set of services to test accessibility from an isolated process
+    private static final String[] RESTRICTED_SERVICES_TO_TEST = {
+            Context.ALARM_SERVICE,
+            Context.WINDOW_SERVICE,
+            Context.POWER_SERVICE
+    };
+
+    private CountDownLatch mLatch;
+    private IIsolatedService mService;
+    private final ServiceConnection mServiceConnection = new ServiceConnection() {
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            Log.e(TAG, "Isolated service " + name + " died abruptly");
+        }
+
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            mService = IIsolatedService.Stub.asInterface(service);
+            mLatch.countDown();
+        }
+    };
+
+    @Override
+    public void setUp() throws InterruptedException {
+        mLatch = new CountDownLatch(1);
+        Intent serviceIntent = new Intent(mContext, IsolatedService.class);
+        mContext.bindService(serviceIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
+        Assert.assertTrue("Timed out while waiting to bind to isolated service",
+                mLatch.await(BIND_SERVICE_TIMEOUT, TimeUnit.MILLISECONDS));
+    }
+
+    public void testGetCachedServicesFromIsolatedService() throws RemoteException {
+        String[] cachedServices = mService.getCachedSystemServices();
+        for (String serviceName : cachedServices) {
+            Assert.assertTrue(serviceName + " should not be accessbible from an isolated process",
+                    ArrayUtils.contains(SERVICES_ALLOWED_TO_ISOLATED_PROCESS, serviceName));
+        }
+    }
+
+    public void testGetServiceFromIsolatedService() throws RemoteException {
+        for (String serviceName : RESTRICTED_SERVICES_TO_TEST) {
+            IBinder service = mService.getSystemService(serviceName);
+            Assert.assertNull(serviceName + " should not be accessible from an isolated process",
+                    service);
+        }
+    }
+
+    @Override
+    public void tearDown() {
+        mContext.unbindService(mServiceConnection);
+    }
+
+}
diff --git a/tests/tests/security/src/android/security/cts/IsolatedService.java b/tests/tests/security/src/android/security/cts/IsolatedService.java
new file mode 100644
index 0000000..28d2a65
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/IsolatedService.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.util.Log;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+public class IsolatedService extends Service {
+
+    private static final String TAG = IsolatedService.class.getSimpleName();
+    private static final String SERVICE_MANAGER_CLASS_NAME = "android.os.ServiceManager";
+    private static final String SERVICE_MANAGER_INTERNAL_CACHE_NAME = "sCache";
+    private static final String GET_SERVICE_METHOD_NAME = "getService";
+
+    private String[] getServicesCachedInServiceManager() {
+        ArrayList<String> cachedServices = new ArrayList<String>();
+        try {
+            Class<?> serviceManager = Class.forName(SERVICE_MANAGER_CLASS_NAME);
+            Field cacheField = serviceManager.getDeclaredField(SERVICE_MANAGER_INTERNAL_CACHE_NAME);
+            cacheField.setAccessible(true);
+            HashMap<String, IBinder> sCache = (HashMap<String, IBinder>) cacheField.get(null);
+            for (Map.Entry<String, IBinder> serviceEntry : sCache.entrySet()) {
+                if (serviceEntry.getValue() != null) {
+                    cachedServices.add(serviceEntry.getKey());
+                }
+            }
+        } catch (ClassCastException | ReflectiveOperationException exc) {
+            Log.w(TAG, "Unable to retrieve service manager cache via reflection ", exc);
+        }
+        return cachedServices.toArray(new String[cachedServices.size()]);
+    }
+
+    private IBinder getServiceFromServiceManager(String serviceName) {
+        try {
+            Class<?> serviceManager = Class.forName(SERVICE_MANAGER_CLASS_NAME);
+            Method getServiceMethod =
+                    serviceManager.getDeclaredMethod(GET_SERVICE_METHOD_NAME, String.class);
+            IBinder service = (IBinder) getServiceMethod.invoke(null, serviceName);
+            return service;
+        } catch (ClassCastException | ReflectiveOperationException exc) {
+            Log.w(TAG, "Unable to call ServiceManager.getService() ", exc);
+        }
+        return null;
+    }
+
+    private final IIsolatedService.Stub mBinder = new IIsolatedService.Stub() {
+
+        public String[] getCachedSystemServices() {
+            return getServicesCachedInServiceManager();
+        }
+
+        public IBinder getSystemService(String serviceName) {
+            return getServiceFromServiceManager(serviceName);
+        }
+    };
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mBinder;
+    }
+}