Merge "Add test for SystemActivityMonitoringService" into nyc-car-dev
diff --git a/tests/carservice_test/AndroidManifest.xml b/tests/carservice_test/AndroidManifest.xml
index 56fa11c..d7c7bdf 100644
--- a/tests/carservice_test/AndroidManifest.xml
+++ b/tests/carservice_test/AndroidManifest.xml
@@ -15,7 +15,6 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
         package="com.android.car.carservicetest"
         android:sharedUserId="android.uid.system" >
 
@@ -36,5 +35,12 @@
                 <action android:name="android.car.content.pm.CarAppBlockingPolicyService"/>
             </intent-filter>
         </service>
+        <activity android:name="com.android.car.test.SystemActivityMonitoringServiceTest$ActivityA" />
+        <activity android:name="com.android.car.test.SystemActivityMonitoringServiceTest$ActivityB"
+            android:taskAffinity="com.android.car.carservicetest.activity"/>
+        <activity android:name="com.android.car.test.SystemActivityMonitoringServiceTest$ActivityC"
+            android:process="com.android.car.carservicetest.activityC"/>
+        <activity android:name="com.android.car.test.SystemActivityMonitoringServiceTest$BlockingActivity"
+            android:taskAffinity="com.android.car.carservicetest.block"/>
     </application>
 </manifest>
diff --git a/tests/carservice_test/src/com/android/car/test/SystemActivityMonitoringServiceTest.java b/tests/carservice_test/src/com/android/car/test/SystemActivityMonitoringServiceTest.java
new file mode 100644
index 0000000..3281865
--- /dev/null
+++ b/tests/carservice_test/src/com/android/car/test/SystemActivityMonitoringServiceTest.java
@@ -0,0 +1,228 @@
+/*
+ * 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 com.android.car.test;
+
+import android.app.Activity;
+import android.car.test.VehicleHalEmulator;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.SystemClock;
+
+import com.android.car.SystemActivityMonitoringService;
+import com.android.car.SystemActivityMonitoringService.TopTaskInfoContainer;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleDrivingStatus;
+import com.android.car.vehiclenetwork.VehicleNetworkProto;
+import com.android.car.vehiclenetwork.VehiclePropConfigUtil;
+import com.android.car.vehiclenetwork.VehiclePropValueUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+public class SystemActivityMonitoringServiceTest extends MockedCarTestBase {
+    private static final long TIMEOUT_MS = 3000;
+    private static final long POLL_INTERVAL_MS = 50;
+    private static final Semaphore sAvailable = new Semaphore(0);
+
+    private final DrivingStatusHandler mDrivingStatusHandler = new DrivingStatusHandler();
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        getVehicleHalEmulator().addProperty(VehiclePropConfigUtil.getBuilder(
+                VehicleNetworkConsts.VEHICLE_PROPERTY_DRIVING_STATUS,
+                VehicleNetworkConsts.VehiclePropAccess.VEHICLE_PROP_ACCESS_READ,
+                VehicleNetworkConsts.VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE,
+                VehicleNetworkConsts.VehicleValueType.VEHICLE_VALUE_TYPE_INT32,
+                VehicleNetworkConsts.VehiclePermissionModel.VEHICLE_PERMISSION_SYSTEM_APP_ONLY,
+                0 /*configFlags*/,
+                0 /*sampleRateMax*/, 0 /*sampleRateMin*/).build(),
+                mDrivingStatusHandler);
+    }
+
+    private void init(boolean drivingStatusRestricted) {
+        // Set no restriction to driving status, to avoid CarPackageManagerService to launch a
+        // blocking activity.
+        mDrivingStatusHandler.setDrivingStatusRestricted(drivingStatusRestricted);
+        getVehicleHalEmulator().start();
+        VehicleNetworkProto.VehiclePropValue injectValue = VehiclePropValueUtil.createIntValue(
+                VehicleNetworkConsts.VEHICLE_PROPERTY_DRIVING_STATUS, 0,
+                SystemClock.elapsedRealtimeNanos());
+        getVehicleHalEmulator().injectEvent(injectValue);
+    }
+
+    public void testActivityLaunch() {
+        init(false);
+        List<TopTaskInfoContainer> taskList = new ArrayList<>();
+        SystemActivityMonitoringService systemActivityMonitoringService =
+                new SystemActivityMonitoringService(getContext());
+        systemActivityMonitoringService.registerActivityLaunchListener(
+                new SystemActivityMonitoringService.ActivityLaunchListener() {
+                    @Override
+                    public void onActivityLaunch(
+                            SystemActivityMonitoringService.TopTaskInfoContainer topTask) {
+                        taskList.add(topTask);
+                    }
+                });
+        getContext().startActivity(new Intent(getContext(), ActivityA.class));
+        verifyTopActivityPolling(taskList, 0, new ComponentName(getContext().getPackageName(),
+                ActivityA.class.getName()));
+        sAvailable.release();
+
+        verifyTopActivityPolling(taskList, 1, new ComponentName(getContext().getPackageName(),
+                ActivityB.class.getName()));
+        sAvailable.release();
+
+        verifyTopActivityPolling(taskList, 2, new ComponentName(getContext().getPackageName(),
+                ActivityC.class.getName()));
+    }
+
+    public void testActivityBlocking() {
+        init(false);
+        Semaphore blocked = new Semaphore(0);
+        List<TopTaskInfoContainer> taskList = new ArrayList<>();
+        SystemActivityMonitoringService systemActivityMonitoringService =
+                new SystemActivityMonitoringService(getContext());
+
+        ComponentName blackListedActivity = new ComponentName(getContext().getPackageName(),
+                ActivityC.class.getName());
+        ComponentName blockingActivity = new ComponentName(getContext().getPackageName(),
+                BlockingActivity.class.getName());
+        Intent newActivityIntent = new Intent();
+        newActivityIntent.setComponent(blockingActivity);
+
+        systemActivityMonitoringService.registerActivityLaunchListener(
+                new SystemActivityMonitoringService.ActivityLaunchListener() {
+                    @Override
+                    public void onActivityLaunch(
+                            SystemActivityMonitoringService.TopTaskInfoContainer topTask) {
+                        taskList.add(topTask);
+                        if (topTask.topActivity.equals(blackListedActivity)) {
+                            systemActivityMonitoringService.blockActivity(topTask,
+                                    newActivityIntent);
+                            blocked.release();
+                        }
+                    }
+                });
+        // start a black listed activity
+        getContext().startActivity(new Intent(getContext(), ActivityC.class));
+        // wait for the listener to call blockActivity()
+        try {
+            blocked.tryAcquire(2, TimeUnit.SECONDS);
+        } catch (InterruptedException e) {
+            fail(e.getMessage());
+        }
+        // We should first receive the blackListedActivity launch,
+        // and later the blockActivity launch
+        verifyTopActivityPolling(taskList, 0, blackListedActivity);
+        verifyTopActivityPolling(taskList, 1, blockingActivity);
+    }
+
+    private void verifyTopActivityPolling(
+            List<TopTaskInfoContainer> topTaskList, int i, ComponentName activity) {
+        boolean activityVerified = false;
+        int timeElapsedMs = 0;
+        try {
+            while (!activityVerified && timeElapsedMs <= TIMEOUT_MS) {
+                Thread.sleep(POLL_INTERVAL_MS);
+                timeElapsedMs += POLL_INTERVAL_MS;
+                if (topTaskList.size() <= i) continue;
+                TopTaskInfoContainer topTask = topTaskList.get(i);
+                if (topTask != null && topTask.topActivity.equals(activity)) {
+                    activityVerified = true;
+                    break;
+                }
+            }
+            assertEquals(true, activityVerified);
+        } catch (Exception e) {
+            fail(e.toString());
+        }
+    }
+
+    public static class ActivityA extends Activity {
+        @Override
+        protected void onPostResume() {
+            super.onPostResume();
+            // Wait until the activity launch event is consumed by the listener.
+            try {
+                if (!sAvailable.tryAcquire(2, TimeUnit.SECONDS)) {
+                    fail("Time out");
+                }
+            } catch (Exception e) {
+                fail(e.toString());
+            }
+            Intent intent = new Intent(this, ActivityB.class);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            startActivity(intent);
+        }
+    }
+
+    public static class ActivityB extends Activity {
+        @Override
+        protected void onPostResume() {
+            super.onPostResume();
+            // Wait until the activity launch event is consumed by the listener.
+            try {
+                if (!sAvailable.tryAcquire(2, TimeUnit.SECONDS)) {
+                    fail("Time out");
+                }
+            } catch (Exception e) {
+                fail(e.toString());
+            }
+            Intent intent = new Intent(this, ActivityC.class);
+            startActivity(intent);
+        }
+    }
+
+    public static class ActivityC extends Activity {
+    }
+
+    public static class BlockingActivity extends Activity {
+    }
+
+    private class DrivingStatusHandler implements VehicleHalEmulator.VehicleHalPropertyHandler {
+        int mDrivingStatus =
+                VehicleNetworkConsts.VehicleDrivingStatus.VEHICLE_DRIVING_STATUS_UNRESTRICTED;
+
+        public void setDrivingStatusRestricted(boolean restricted) {
+            mDrivingStatus = restricted ? VehicleDrivingStatus.VEHICLE_DRIVING_STATUS_NO_VIDEO
+                    : VehicleDrivingStatus.VEHICLE_DRIVING_STATUS_UNRESTRICTED;
+        }
+
+        @Override
+        public void onPropertySet(VehicleNetworkProto.VehiclePropValue value) {
+        }
+
+        @Override
+        public VehicleNetworkProto.VehiclePropValue onPropertyGet(
+                VehicleNetworkProto.VehiclePropValue value) {
+            return VehiclePropValueUtil.createIntValue(
+                    VehicleNetworkConsts.VEHICLE_PROPERTY_DRIVING_STATUS,
+                    mDrivingStatus,
+                    SystemClock.elapsedRealtimeNanos());
+        }
+
+        @Override
+        public void onPropertySubscribe(int property, float sampleRate, int zones) {
+        }
+
+        @Override
+        public void onPropertyUnsubscribe(int property) {
+        }
+    }
+}
\ No newline at end of file