1 /*
2  * Copyright (C) 2019 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  */
16 
17 package com.android.car;
18 
19 import static org.junit.Assert.fail;
20 import static org.mockito.ArgumentMatchers.any;
21 import static org.mockito.ArgumentMatchers.anyInt;
22 import static org.mockito.ArgumentMatchers.anyString;
23 import static org.mockito.ArgumentMatchers.eq;
24 import static org.mockito.Mockito.doReturn;
25 import static org.mockito.Mockito.doThrow;
26 import static org.mockito.Mockito.spy;
27 import static org.mockito.Mockito.when;
28 
29 import android.automotive.powerpolicy.internal.ICarPowerPolicyDelegate;
30 import android.car.Car;
31 import android.car.ICarResultReceiver;
32 import android.car.feature.Flags;
33 import android.content.Context;
34 import android.content.res.Resources;
35 import android.frameworks.automotive.powerpolicy.internal.ICarPowerPolicySystemNotification;
36 import android.os.Bundle;
37 import android.os.HandlerThread;
38 import android.os.IInterface;
39 import android.os.Looper;
40 import android.os.Process;
41 import android.os.UserHandle;
42 import android.os.UserManager;
43 import android.util.Log;
44 
45 import androidx.test.platform.app.InstrumentationRegistry;
46 
47 import com.android.car.garagemode.GarageModeService;
48 import com.android.car.hal.HalPropValueBuilder;
49 import com.android.car.hal.PowerHalService;
50 import com.android.car.internal.ICarServiceHelper;
51 import com.android.car.internal.StaticBinderInterface;
52 import com.android.car.os.CarPerformanceService;
53 import com.android.car.remoteaccess.CarRemoteAccessService;
54 import com.android.car.systeminterface.ActivityManagerInterface;
55 import com.android.car.systeminterface.DisplayInterface;
56 import com.android.car.systeminterface.IOInterface;
57 import com.android.car.systeminterface.StorageMonitoringInterface;
58 import com.android.car.systeminterface.SystemInterface;
59 import com.android.car.systeminterface.SystemStateInterface;
60 import com.android.car.systeminterface.TimeInterface;
61 import com.android.car.systeminterface.WakeLockInterface;
62 import com.android.car.telemetry.CarTelemetryService;
63 import com.android.car.test.utils.TemporaryDirectory;
64 import com.android.car.watchdog.CarWatchdogService;
65 
66 import org.junit.After;
67 import org.junit.Before;
68 import org.junit.Test;
69 import org.junit.runner.RunWith;
70 import org.mockito.Mock;
71 import org.mockito.junit.MockitoJUnitRunner;
72 
73 import java.io.File;
74 import java.io.IOException;
75 
76 /**
77  * This class contains unit tests for the {@link ICarImpl}.
78  *
79  * <p>It tests that services started with {@link ICarImpl} are initialized properly.
80  * <p>The following mocks are used:
81  * <ol>
82  * <li>{@link ActivityManagerInterface} broadcasts intent for a user.</li>
83  * <li>{@link DisplayInterface} provides access to display operations.</li>
84  * <li>{@link IVehicle} provides access to vehicle properties.</li>
85  * <li>{@link StorageMonitoringInterface} provides access to storage monitoring operations.</li>
86  * <li>{@link SystemStateInterface} provides system statuses (booting, sleeping, ...).</li>
87  * <li>{@link TimeInterface} provides access to time operations.</li>
88  * <li>{@link TimeInterface} provides access to wake lock operations.</li>
89  * </ol>
90  */
91 @RunWith(MockitoJUnitRunner.class)
92 public final class ICarImplTest {
93     private static final String TAG = ICarImplTest.class.getSimpleName();
94 
95     @Mock private ActivityManagerInterface mMockActivityManagerInterface;
96     @Mock private DisplayInterface mMockDisplayInterface;
97     @Mock private VehicleStub mMockVehicle;
98     @Mock private StorageMonitoringInterface mMockStorageMonitoringInterface;
99     @Mock private SystemStateInterface mMockSystemStateInterface;
100     @Mock private TimeInterface mMockTimeInterface;
101     @Mock private WakeLockInterface mMockWakeLockInterface;
102     @Mock private CarWatchdogService mMockCarWatchdogService;
103     @Mock private CarPerformanceService mMockCarPerformanceService;
104     @Mock private GarageModeService mMockGarageModeService;
105     @Mock private ICarPowerPolicySystemNotification.Stub mMockCarPowerPolicyDaemon;
106     @Mock private ICarPowerPolicyDelegate.Stub mMockRefactoredCarPowerPolicyDaemon;
107     @Mock private CarTelemetryService mMockCarTelemetryService;
108     @Mock private CarRemoteAccessService mMockCarRemoteAccessService;
109     @Mock private ICarServiceHelper mICarServiceHelper;
110 
111     private Context mContext;
112     private SystemInterface mFakeSystemInterface;
113     private UserManager mUserManager;
114 
115     private final MockIOInterface mMockIOInterface = new MockIOInterface();
116 
117     static final class CarServiceConnectedCallback extends ICarResultReceiver.Stub {
118         @Override
send(int resultCode, Bundle resultData)119         public void send(int resultCode, Bundle resultData) {
120             Log.i(TAG, "CarServiceConnectedCallback.send(int resultCode, Bundle resultData)");
121         }
122     }
123 
124     /**
125      * Initialize all of the objects with the @Mock annotation.
126      */
127     @Before
setUp()128     public void setUp() throws Exception {
129         // InstrumentationTestRunner prepares a looper, but AndroidJUnitRunner does not.
130         // http://b/25897652.
131         if (Looper.myLooper() == null) {
132             Looper.prepare();
133         }
134 
135         mContext = spy(InstrumentationRegistry.getInstrumentation().getTargetContext());
136 
137         mUserManager = spy(mContext.getSystemService(UserManager.class));
138         doReturn(mUserManager).when(mContext).getSystemService(eq(UserManager.class));
139         doReturn(mUserManager).when(mContext).getSystemService(eq(Context.USER_SERVICE));
140 
141         Resources resources = spy(mContext.getResources());
142         doReturn("").when(resources).getString(
143                 eq(com.android.car.R.string.instrumentClusterRendererService));
144         doReturn(false).when(resources).getBoolean(
145                 eq(com.android.car.R.bool.audioUseDynamicRouting));
146         doReturn(new String[0]).when(resources).getStringArray(
147                 eq(com.android.car.R.array.config_earlyStartupServices));
148         doReturn(resources).when(mContext).getResources();
149 
150         mFakeSystemInterface = SystemInterface.Builder.newSystemInterface()
151                 .withSystemStateInterface(mMockSystemStateInterface)
152                 .withActivityManagerInterface(mMockActivityManagerInterface)
153                 .withDisplayInterface(mMockDisplayInterface)
154                 .withIOInterface(mMockIOInterface)
155                 .withStorageMonitoringInterface(mMockStorageMonitoringInterface)
156                 .withTimeInterface(mMockTimeInterface)
157                 .withWakeLockInterface(mMockWakeLockInterface).build();
158         // ICarImpl will register new CarLocalServices services.
159         // This prevents one test failure in tearDown from triggering assertion failure for single
160         // CarLocalServices service.
161         CarLocalServices.removeAllServices();
162     }
163 
164     /**
165      *  Clean up before running the next test.
166      */
167     @After
tearDown()168     public void tearDown() {
169         try {
170             if (mMockIOInterface != null) {
171                 mMockIOInterface.tearDown();
172             }
173         } finally {
174             CarLocalServices.removeAllServices();
175         }
176     }
177 
178     @Test
testNoShardedPreferencesAccessedBeforeUserZeroUnlock()179     public void testNoShardedPreferencesAccessedBeforeUserZeroUnlock() throws Exception {
180         doReturn(true).when(mContext).isCredentialProtectedStorage();
181         doReturn(false).when(mUserManager).isUserUnlockingOrUnlocked(anyInt());
182         doReturn(false).when(mUserManager).isUserUnlocked();
183         doReturn(false).when(mUserManager).isUserUnlocked(anyInt());
184         doReturn(false).when(mUserManager).isUserUnlocked(any(UserHandle.class));
185         doReturn(false).when(mUserManager).isUserUnlockingOrUnlocked(any(UserHandle.class));
186 
187         doThrow(new NullPointerException()).when(mContext).getSharedPrefsFile(anyString());
188         doThrow(new NullPointerException()).when(mContext).getSharedPreferencesPath(any());
189         doThrow(new NullPointerException()).when(mContext).getSharedPreferences(
190                 anyString(), anyInt());
191         doThrow(new NullPointerException()).when(mContext).getSharedPreferences(
192                 any(File.class), anyInt());
193         doThrow(new NullPointerException()).when(mContext).getDataDir();
194         IInterface powerPolicyDaemon;
195         if (Flags.carPowerPolicyRefactoring()) {
196             powerPolicyDaemon = mMockRefactoredCarPowerPolicyDaemon;
197         } else {
198             powerPolicyDaemon = mMockCarPowerPolicyDaemon;
199         }
200         when(mMockVehicle.getHalPropValueBuilder()).thenReturn(
201                 new HalPropValueBuilder(/*isAidl=*/true));
202         ICarImpl carImpl = new ICarImpl.Builder()
203                 .setServiceContext(mContext)
204                 .setVehicle(mMockVehicle)
205                 .setVehicleInterfaceName("MockedCar")
206                 .setSystemInterface(mFakeSystemInterface)
207                 .setCarWatchdogService(mMockCarWatchdogService)
208                 .setCarPerformanceService(mMockCarPerformanceService)
209                 .setCarTelemetryService(mMockCarTelemetryService)
210                 .setCarRemoteAccessServiceConstructor((
211                         Context context, SystemInterface systemInterface,
212                         PowerHalService powerHalService
213                     ) -> mMockCarRemoteAccessService)
214                 .setGarageModeService(mMockGarageModeService)
215                 .setPowerPolicyDaemon(powerPolicyDaemon)
216                 .setDoPriorityInitInConstruction(false)
217                 .setTestStaticBinder(new StaticBinderInterface() {
218                     @Override
219                     public int getCallingUid() {
220                         return Process.SYSTEM_UID;
221                     }
222 
223                     @Override
224                     public int getCallingPid() {
225                         return 0;
226                     }
227                 })
228                 .build();
229 
230         carImpl.setSystemServerConnections(mICarServiceHelper, new CarServiceConnectedCallback());
231         carImpl.init();
232         Car mCar = new Car(mContext, carImpl, /* handler= */ null);
233 
234         // Post tasks for Handler Threads to ensure all the tasks that will be queued inside init
235         // will be done.
236         for (Thread t : Thread.getAllStackTraces().keySet()) {
237             if (!HandlerThread.class.isInstance(t)) {
238                 continue;
239             }
240             HandlerThread ht = (HandlerThread) t;
241             CarServiceUtils.runOnLooperSync(ht.getLooper(), () -> {
242                 // Do nothing, just need to make sure looper finishes current task.
243             });
244         }
245 
246         mCar.disconnect();
247         carImpl.release();
248     }
249 
250     static final class MockIOInterface implements IOInterface {
251         private TemporaryDirectory mFilesDir = null;
252 
253         @Override
getSystemCarDir()254         public File getSystemCarDir() {
255             if (mFilesDir == null) {
256                 try {
257                     mFilesDir = new TemporaryDirectory(TAG);
258                 } catch (IOException e) {
259                     Log.e(TAG, "failed to create temporary directory", e);
260                     fail("failed to create temporary directory. exception was: " + e);
261                 }
262             }
263             return mFilesDir.getDirectory();
264         }
265 
tearDown()266         public void tearDown() {
267             if (mFilesDir != null) {
268                 try {
269                     mFilesDir.close();
270                 } catch (Exception e) {
271                     Log.w(TAG, "could not remove temporary directory", e);
272                 }
273             }
274         }
275     }
276 }
277