1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.android.car.test;
17
18import android.app.Activity;
19import android.content.ComponentName;
20import android.content.Intent;
21import android.hardware.automotive.vehicle.V2_0.VehicleDrivingStatus;
22import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
23import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
24import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess;
25import android.os.SystemClock;
26
27import com.android.car.SystemActivityMonitoringService;
28import com.android.car.SystemActivityMonitoringService.TopTaskInfoContainer;
29import com.android.car.vehiclehal.VehiclePropValueBuilder;
30import com.android.car.vehiclehal.test.MockedVehicleHal.VehicleHalPropertyHandler;
31
32import java.util.ArrayList;
33import java.util.List;
34import java.util.concurrent.Semaphore;
35import java.util.concurrent.TimeUnit;
36
37public class SystemActivityMonitoringServiceTest extends MockedCarTestBase {
38    private static final long TIMEOUT_MS = 3000;
39    private static final long POLL_INTERVAL_MS = 50;
40    private static final Semaphore sAvailable = new Semaphore(0);
41
42    private final DrivingStatusHandler mDrivingStatusHandler = new DrivingStatusHandler();
43
44    @Override
45    protected synchronized void configureMockedHal() {
46        addProperty(VehicleProperty.DRIVING_STATUS, mDrivingStatusHandler)
47                .setAccess(VehiclePropertyAccess.READ);
48    }
49
50    private void init(boolean drivingStatusRestricted) {
51        // Set no restriction to driving status, to avoid CarPackageManagerService to launch a
52        // blocking activity.
53        mDrivingStatusHandler.setDrivingStatusRestricted(drivingStatusRestricted);
54
55        VehiclePropValue injectValue =
56                VehiclePropValueBuilder.newBuilder(VehicleProperty.DRIVING_STATUS)
57                        .setTimestamp(SystemClock.elapsedRealtimeNanos())
58                        .addIntValue(0)
59                        .build();
60        getMockedVehicleHal().injectEvent(injectValue);
61    }
62
63    public void testActivityLaunch() {
64        init(false);
65        List<TopTaskInfoContainer> taskList = new ArrayList<>();
66        SystemActivityMonitoringService systemActivityMonitoringService =
67                new SystemActivityMonitoringService(getContext());
68        systemActivityMonitoringService.registerActivityLaunchListener(
69                new SystemActivityMonitoringService.ActivityLaunchListener() {
70                    @Override
71                    public void onActivityLaunch(
72                            SystemActivityMonitoringService.TopTaskInfoContainer topTask) {
73                        taskList.add(topTask);
74                    }
75                });
76        getContext().startActivity(new Intent(getContext(), ActivityA.class));
77        verifyTopActivityPolling(taskList, 0, new ComponentName(getContext().getPackageName(),
78                ActivityA.class.getName()));
79        sAvailable.release();
80
81        verifyTopActivityPolling(taskList, 1, new ComponentName(getContext().getPackageName(),
82                ActivityB.class.getName()));
83        sAvailable.release();
84
85        verifyTopActivityPolling(taskList, 2, new ComponentName(getContext().getPackageName(),
86                ActivityC.class.getName()));
87    }
88
89    public void testActivityBlocking() {
90        init(false);
91        Semaphore blocked = new Semaphore(0);
92        List<TopTaskInfoContainer> taskList = new ArrayList<>();
93        SystemActivityMonitoringService systemActivityMonitoringService =
94                new SystemActivityMonitoringService(getContext());
95
96        ComponentName blackListedActivity = new ComponentName(getContext().getPackageName(),
97                ActivityC.class.getName());
98        ComponentName blockingActivity = new ComponentName(getContext().getPackageName(),
99                BlockingActivity.class.getName());
100        Intent newActivityIntent = new Intent();
101        newActivityIntent.setComponent(blockingActivity);
102
103        systemActivityMonitoringService.registerActivityLaunchListener(
104                new SystemActivityMonitoringService.ActivityLaunchListener() {
105                    @Override
106                    public void onActivityLaunch(
107                            SystemActivityMonitoringService.TopTaskInfoContainer topTask) {
108                        taskList.add(topTask);
109                        if (topTask.topActivity.equals(blackListedActivity)) {
110                            systemActivityMonitoringService.blockActivity(topTask,
111                                    newActivityIntent);
112                            blocked.release();
113                        }
114                    }
115                });
116        // start a black listed activity
117        getContext().startActivity(new Intent(getContext(), ActivityC.class));
118        // wait for the listener to call blockActivity()
119        try {
120            blocked.tryAcquire(2, TimeUnit.SECONDS);
121        } catch (InterruptedException e) {
122            fail(e.getMessage());
123        }
124        // We should first receive the blackListedActivity launch,
125        // and later the blockActivity launch
126        verifyTopActivityPolling(taskList, 0, blackListedActivity);
127        verifyTopActivityPolling(taskList, 1, blockingActivity);
128    }
129
130    private void verifyTopActivityPolling(
131            List<TopTaskInfoContainer> topTaskList, int i, ComponentName activity) {
132        boolean activityVerified = false;
133        int timeElapsedMs = 0;
134        try {
135            while (!activityVerified && timeElapsedMs <= TIMEOUT_MS) {
136                Thread.sleep(POLL_INTERVAL_MS);
137                timeElapsedMs += POLL_INTERVAL_MS;
138                if (topTaskList.size() <= i) continue;
139                TopTaskInfoContainer topTask = topTaskList.get(i);
140                if (topTask != null && topTask.topActivity.equals(activity)) {
141                    activityVerified = true;
142                    break;
143                }
144            }
145            assertEquals(true, activityVerified);
146        } catch (Exception e) {
147            fail(e.toString());
148        }
149    }
150
151    public static class ActivityA extends Activity {
152        @Override
153        protected void onPostResume() {
154            super.onPostResume();
155            // Wait until the activity launch event is consumed by the listener.
156            try {
157                if (!sAvailable.tryAcquire(2, TimeUnit.SECONDS)) {
158                    fail("Time out");
159                }
160            } catch (Exception e) {
161                fail(e.toString());
162            }
163            Intent intent = new Intent(this, ActivityB.class);
164            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
165            startActivity(intent);
166        }
167    }
168
169    public static class ActivityB extends Activity {
170        @Override
171        protected void onPostResume() {
172            super.onPostResume();
173            // Wait until the activity launch event is consumed by the listener.
174            try {
175                if (!sAvailable.tryAcquire(2, TimeUnit.SECONDS)) {
176                    fail("Time out");
177                }
178            } catch (Exception e) {
179                fail(e.toString());
180            }
181            Intent intent = new Intent(this, ActivityC.class);
182            startActivity(intent);
183        }
184    }
185
186    public static class ActivityC extends Activity {
187    }
188
189    public static class BlockingActivity extends Activity {
190    }
191
192    private class DrivingStatusHandler implements VehicleHalPropertyHandler {
193        int mDrivingStatus = VehicleDrivingStatus.UNRESTRICTED;
194
195        public void setDrivingStatusRestricted(boolean restricted) {
196            mDrivingStatus = restricted ? VehicleDrivingStatus.NO_VIDEO
197                    : VehicleDrivingStatus.UNRESTRICTED;
198        }
199
200        @Override
201        public void onPropertySet(VehiclePropValue value) {
202        }
203
204        @Override
205        public VehiclePropValue onPropertyGet(VehiclePropValue value) {
206            return VehiclePropValueBuilder.newBuilder(VehicleProperty.DRIVING_STATUS)
207                    .setTimestamp(SystemClock.elapsedRealtimeNanos())
208                    .addIntValue(mDrivingStatus)
209                    .build();
210        }
211
212        @Override
213        public void onPropertySubscribe(int property, int zones, float sampleRate) {
214        }
215
216        @Override
217        public void onPropertyUnsubscribe(int property) {
218        }
219    }
220}