1 /* 2 * Copyright (C) 2022 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.server.devicelock; 18 19 import static android.app.AppOpsManager.OPSTR_SYSTEM_EXEMPT_FROM_HIBERNATION; 20 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; 21 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; 22 import static android.devicelock.DeviceId.DEVICE_ID_TYPE_IMEI; 23 import static android.devicelock.DeviceId.DEVICE_ID_TYPE_MEID; 24 import static android.devicelock.IDeviceLockService.KEY_REMOTE_CALLBACK_RESULT; 25 import static android.os.UserHandle.USER_SYSTEM; 26 27 import static com.android.server.devicelock.DeviceLockControllerPackageUtils.SERVICE_ACTION; 28 import static com.android.server.devicelock.DeviceLockServiceImpl.MANAGE_DEVICE_LOCK_SERVICE_FROM_CONTROLLER; 29 import static com.android.server.devicelock.DeviceLockServiceImpl.OPSTR_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION; 30 import static com.android.server.devicelock.DeviceLockServiceImpl.OPSTR_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS; 31 import static com.android.server.devicelock.DeviceLockServiceImpl.OPSTR_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS; 32 import static com.android.server.devicelock.TestUtils.eventually; 33 34 import static com.google.common.truth.Truth.assertThat; 35 36 import static org.mockito.ArgumentMatchers.any; 37 import static org.mockito.ArgumentMatchers.eq; 38 import static org.mockito.Mockito.doAnswer; 39 import static org.mockito.Mockito.mock; 40 import static org.mockito.Mockito.timeout; 41 import static org.mockito.Mockito.verify; 42 import static org.robolectric.Shadows.shadowOf; 43 44 import android.app.AppOpsManager; 45 import android.app.Application; 46 import android.content.ComponentName; 47 import android.content.Context; 48 import android.content.Intent; 49 import android.content.ServiceConnection; 50 import android.content.pm.ApplicationInfo; 51 import android.content.pm.PackageInfo; 52 import android.content.pm.PackageManager; 53 import android.content.pm.ResolveInfo; 54 import android.content.pm.ServiceInfo; 55 import android.devicelock.IGetDeviceIdCallback; 56 import android.os.Binder; 57 import android.os.Bundle; 58 import android.os.Looper; 59 import android.os.PowerExemptionManager; 60 import android.os.Process; 61 import android.os.RemoteCallback; 62 import android.os.UserHandle; 63 import android.os.UserManager; 64 import android.telephony.TelephonyManager; 65 66 import androidx.test.core.app.ApplicationProvider; 67 68 import com.android.devicelockcontroller.IDeviceLockControllerService; 69 70 import org.junit.Before; 71 import org.junit.Rule; 72 import org.junit.Test; 73 import org.junit.runner.RunWith; 74 import org.mockito.Mock; 75 import org.mockito.junit.MockitoJUnit; 76 import org.mockito.junit.MockitoRule; 77 import org.mockito.stubbing.Answer; 78 import org.robolectric.RobolectricTestRunner; 79 import org.robolectric.shadows.ShadowAppOpsManager; 80 import org.robolectric.shadows.ShadowApplication; 81 import org.robolectric.shadows.ShadowBinder; 82 import org.robolectric.shadows.ShadowPackageManager; 83 import org.robolectric.shadows.ShadowTelephonyManager; 84 import org.robolectric.shadows.ShadowUserManager; 85 86 import java.util.concurrent.ExecutionException; 87 import java.util.concurrent.ExecutorService; 88 import java.util.concurrent.Executors; 89 import java.util.concurrent.atomic.AtomicBoolean; 90 91 /** 92 * Tests for {@link com.android.server.devicelock.DeviceLockServiceImpl}. 93 * 94 * TODO(b/329330992): Add tests for multi-user scenarios where users have different finalization 95 * states. Robolectric does not support creating contexts as other users, so the package manager 96 * infos are the same for all users. This makes it infeasible to unit test scenarios where the 97 * package states are different for different users. 98 * 99 */ 100 @RunWith(RobolectricTestRunner.class) 101 public final class DeviceLockServiceImplTest { 102 private static final String DLC_PACKAGE_NAME = "test.package"; 103 104 private static final String DLC_SERVICE_NAME = "test.service"; 105 106 private static final String SYSTEM_USER_NAME = "system"; 107 private static final int USER_SECONDARY = 10; 108 private static final String SECONDARY_USER_NAME = "secondary"; 109 110 private static final long ONE_SEC_MILLIS = 1000; 111 112 @Rule 113 public MockitoRule mMockitoRule = MockitoJUnit.rule(); 114 115 private Context mContext; 116 private ShadowTelephonyManager mShadowTelephonyManager; 117 private ShadowAppOpsManager mShadowAppOpsManager; 118 private ShadowPackageManager mShadowPackageManager; 119 private PackageManager mPackageManager; 120 private ShadowUserManager mShadowUserManager; 121 private UserHandle mSystemUser; 122 private UserHandle mSecondaryUser; 123 124 @Mock 125 private IDeviceLockControllerService mDeviceLockControllerService; 126 @Mock 127 private PowerExemptionManager mPowerExemptionManager; 128 129 private ShadowApplication mShadowApplication; 130 131 private DeviceLockServiceImpl mService; 132 private final ExecutorService mExecutorService = Executors.newSingleThreadExecutor(); 133 134 @Before setup()135 public void setup() throws Exception { 136 mContext = ApplicationProvider.getApplicationContext(); 137 mShadowApplication = shadowOf((Application) mContext); 138 mShadowApplication.grantPermissions(MANAGE_DEVICE_LOCK_SERVICE_FROM_CONTROLLER); 139 mShadowApplication.setSystemService( 140 mContext.getSystemServiceName(PowerExemptionManager.class), 141 mPowerExemptionManager); 142 143 mPackageManager = mContext.getPackageManager(); 144 mShadowPackageManager = shadowOf(mPackageManager); 145 mShadowPackageManager.setPackagesForUid(Process.myUid(), 146 new String[]{mContext.getPackageName()}); 147 148 PackageInfo dlcPackageInfo = new PackageInfo(); 149 dlcPackageInfo.packageName = DLC_PACKAGE_NAME; 150 mShadowPackageManager.installPackage(dlcPackageInfo); 151 152 Intent intent = new Intent(SERVICE_ACTION); 153 ResolveInfo resolveInfo = makeDlcResolveInfo(); 154 mShadowPackageManager.addResolveInfoForIntent(intent, resolveInfo); 155 156 TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class); 157 mShadowTelephonyManager = shadowOf(telephonyManager); 158 159 mShadowAppOpsManager = shadowOf(mContext.getSystemService(AppOpsManager.class)); 160 161 mShadowUserManager = shadowOf(mContext.getSystemService(UserManager.class)); 162 mSystemUser = mShadowUserManager.addUser(USER_SYSTEM, SYSTEM_USER_NAME, /* flags= */ 0); 163 mSecondaryUser = mShadowUserManager.addUser(USER_SECONDARY, SECONDARY_USER_NAME, 164 /* flags= */ 0); 165 166 mService = new DeviceLockServiceImpl(mContext, telephonyManager, mExecutorService, 167 mContext.getFilesDir()); 168 waitUntilBgExecutorIdle(); 169 shadowOf(Looper.getMainLooper()).idle(); 170 } 171 172 @Test getDeviceId_withIMEIType_shouldReturnIMEI()173 public void getDeviceId_withIMEIType_shouldReturnIMEI() throws Exception { 174 // GIVEN an IMEI registered in telephony manager 175 final String testImei = "983402979622353"; 176 mShadowTelephonyManager.setActiveModemCount(1); 177 mShadowTelephonyManager.setImei(/* slotIndex= */ 0, testImei); 178 mShadowPackageManager.setSystemFeature(PackageManager.FEATURE_TELEPHONY_GSM, 179 /* supported= */ true); 180 181 // GIVEN a successful service call to DLC app 182 doAnswer((Answer<Void>) invocation -> { 183 RemoteCallback callback = invocation.getArgument(0); 184 Bundle bundle = new Bundle(); 185 bundle.putString(IDeviceLockControllerService.KEY_RESULT, testImei); 186 callback.sendResult(bundle); 187 return null; 188 }).when(mDeviceLockControllerService).getDeviceIdentifier(any(RemoteCallback.class)); 189 190 IGetDeviceIdCallback mockCallback = mock(IGetDeviceIdCallback.class); 191 192 // WHEN the device id is requested with the IMEI device type 193 mService.getDeviceId(mockCallback, 1 << DEVICE_ID_TYPE_IMEI); 194 waitUntilConnected(); 195 196 // THEN the IMEI id is received 197 verify(mockCallback, timeout(ONE_SEC_MILLIS)).onDeviceIdReceived( 198 eq(DEVICE_ID_TYPE_IMEI), eq(testImei)); 199 } 200 201 @Test getDeviceId_withMEIDType_shouldReturnMEID()202 public void getDeviceId_withMEIDType_shouldReturnMEID() throws Exception { 203 // GIVEN an MEID registered in telephony manager 204 final String testMeid = "354403064522046"; 205 mShadowTelephonyManager.setActiveModemCount(1); 206 mShadowTelephonyManager.setMeid(/* slotIndex= */ 0, testMeid); 207 mShadowPackageManager.setSystemFeature(PackageManager.FEATURE_TELEPHONY_CDMA, 208 /* supported= */ true); 209 210 // GIVEN a successful service call to DLC app 211 doAnswer((Answer<Void>) invocation -> { 212 RemoteCallback callback = invocation.getArgument(0); 213 Bundle bundle = new Bundle(); 214 bundle.putString(IDeviceLockControllerService.KEY_RESULT, testMeid); 215 callback.sendResult(bundle); 216 return null; 217 }).when(mDeviceLockControllerService).getDeviceIdentifier(any(RemoteCallback.class)); 218 219 IGetDeviceIdCallback mockCallback = mock(IGetDeviceIdCallback.class); 220 221 // WHEN the device id is requested with the MEID device type 222 mService.getDeviceId(mockCallback, 1 << DEVICE_ID_TYPE_MEID); 223 waitUntilConnected(); 224 225 // THEN the MEID id is received 226 verify(mockCallback, timeout(ONE_SEC_MILLIS)).onDeviceIdReceived( 227 eq(DEVICE_ID_TYPE_MEID), eq(testMeid)); 228 } 229 230 @Test setCallerAllowedToSendUndismissibleNotifications_trueAllowsAppOp()231 public void setCallerAllowedToSendUndismissibleNotifications_trueAllowsAppOp() { 232 final AtomicBoolean succeeded = new AtomicBoolean(false); 233 RemoteCallback callback = new RemoteCallback(result -> { 234 succeeded.set(result.getBoolean(KEY_REMOTE_CALLBACK_RESULT)); 235 }); 236 mService.setCallerAllowedToSendUndismissibleNotifications(true, callback); 237 238 assertThat(succeeded.get()).isTrue(); 239 final int opMode = mShadowAppOpsManager.unsafeCheckOpNoThrow( 240 OPSTR_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS, 241 Process.myUid(), 242 DLC_PACKAGE_NAME); 243 assertThat(opMode).isEqualTo(AppOpsManager.MODE_ALLOWED); 244 } 245 246 @Test setCallerAllowedToSendUndismissibleNotifications_falseDisallowsAppOp()247 public void setCallerAllowedToSendUndismissibleNotifications_falseDisallowsAppOp() { 248 final AtomicBoolean succeeded = new AtomicBoolean(false); 249 RemoteCallback callback = new RemoteCallback(result -> { 250 succeeded.set(result.getBoolean(KEY_REMOTE_CALLBACK_RESULT)); 251 }); 252 mService.setCallerAllowedToSendUndismissibleNotifications(false, callback); 253 254 assertThat(succeeded.get()).isTrue(); 255 final int opMode = mShadowAppOpsManager.unsafeCheckOpNoThrow( 256 OPSTR_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS, 257 Process.myUid(), 258 DLC_PACKAGE_NAME); 259 assertThat(opMode).isEqualTo(AppOpsManager.MODE_DEFAULT); 260 } 261 262 @Test setCallerExemptFromActivityBgStartRestrictionState_trueAllowsAppOp()263 public void setCallerExemptFromActivityBgStartRestrictionState_trueAllowsAppOp() { 264 final AtomicBoolean succeeded = new AtomicBoolean(false); 265 RemoteCallback callback = new RemoteCallback(result -> { 266 succeeded.set(result.getBoolean(KEY_REMOTE_CALLBACK_RESULT)); 267 }); 268 mService.setCallerExemptFromActivityBgStartRestrictionState(true, callback); 269 270 assertThat(succeeded.get()).isTrue(); 271 final int opMode = mShadowAppOpsManager.unsafeCheckOpNoThrow( 272 OPSTR_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION, 273 Process.myUid(), 274 DLC_PACKAGE_NAME); 275 assertThat(opMode).isEqualTo(AppOpsManager.MODE_ALLOWED); 276 } 277 278 @Test setCallerExemptFromActivityBgStartRestrictionState_falseDisallowsAppOp()279 public void setCallerExemptFromActivityBgStartRestrictionState_falseDisallowsAppOp() { 280 final AtomicBoolean succeeded = new AtomicBoolean(false); 281 RemoteCallback callback = new RemoteCallback(result -> { 282 succeeded.set(result.getBoolean(KEY_REMOTE_CALLBACK_RESULT)); 283 }); 284 mService.setCallerExemptFromActivityBgStartRestrictionState(false, callback); 285 286 assertThat(succeeded.get()).isTrue(); 287 final int opMode = mShadowAppOpsManager.unsafeCheckOpNoThrow( 288 OPSTR_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION, 289 Process.myUid(), 290 DLC_PACKAGE_NAME); 291 assertThat(opMode).isEqualTo(AppOpsManager.MODE_DEFAULT); 292 } 293 294 @Test setUidExemptFromRestrictionsState_trueAllowsAppOps()295 public void setUidExemptFromRestrictionsState_trueAllowsAppOps() { 296 final AtomicBoolean succeeded = new AtomicBoolean(false); 297 RemoteCallback callback = new RemoteCallback(result -> { 298 succeeded.set(result.getBoolean(KEY_REMOTE_CALLBACK_RESULT)); 299 }); 300 mService.setUidExemptFromRestrictionsState(Process.myUid(), true, callback); 301 302 assertThat(succeeded.get()).isTrue(); 303 final int hibernationOpMode = mShadowAppOpsManager.unsafeCheckOpNoThrow( 304 OPSTR_SYSTEM_EXEMPT_FROM_HIBERNATION, 305 Process.myUid(), 306 mContext.getPackageName()); 307 assertThat(hibernationOpMode).isEqualTo(AppOpsManager.MODE_ALLOWED); 308 final int powerOpMode = mShadowAppOpsManager.unsafeCheckOpNoThrow( 309 OPSTR_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS, 310 Process.myUid(), 311 mContext.getPackageName()); 312 assertThat(powerOpMode).isEqualTo(AppOpsManager.MODE_ALLOWED); 313 } 314 315 @Test setUidExemptFromRestrictionsState_falseDisallowsAppOps()316 public void setUidExemptFromRestrictionsState_falseDisallowsAppOps() { 317 final AtomicBoolean succeeded = new AtomicBoolean(false); 318 RemoteCallback callback = new RemoteCallback(result -> { 319 succeeded.set(result.getBoolean(KEY_REMOTE_CALLBACK_RESULT)); 320 }); 321 mService.setUidExemptFromRestrictionsState(Process.myUid(), false, callback); 322 323 assertThat(succeeded.get()).isTrue(); 324 final int hibernationOpMode = mShadowAppOpsManager.unsafeCheckOpNoThrow( 325 OPSTR_SYSTEM_EXEMPT_FROM_HIBERNATION, 326 Process.myUid(), 327 mContext.getPackageName()); 328 assertThat(hibernationOpMode).isEqualTo(AppOpsManager.MODE_DEFAULT); 329 final int powerOpMode = mShadowAppOpsManager.unsafeCheckOpNoThrow( 330 OPSTR_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS, 331 Process.myUid(), 332 mContext.getPackageName()); 333 assertThat(powerOpMode).isEqualTo(AppOpsManager.MODE_DEFAULT); 334 } 335 336 @Test setDeviceFinalized_nonSystemUser_disablesPackage()337 public void setDeviceFinalized_nonSystemUser_disablesPackage() throws Exception { 338 ShadowBinder.setCallingUserHandle(mSecondaryUser); 339 340 AtomicBoolean succeeded = new AtomicBoolean(false); 341 mService.setDeviceFinalized(true, new RemoteCallback(result -> succeeded.set(true))); 342 waitUntilBgExecutorIdle(); 343 344 assertThat(succeeded.get()).isTrue(); 345 assertThat(mPackageManager.getApplicationEnabledSetting(DLC_PACKAGE_NAME)) 346 .isEqualTo(COMPONENT_ENABLED_STATE_DISABLED); 347 } 348 349 @Test setDeviceFinalized_systemUser_butOtherUserUnfinalized_doesNotDisablePackage()350 public void setDeviceFinalized_systemUser_butOtherUserUnfinalized_doesNotDisablePackage() 351 throws Exception { 352 ShadowBinder.setCallingUserHandle(mSystemUser); 353 354 AtomicBoolean succeeded = new AtomicBoolean(false); 355 mService.setDeviceFinalized(true, new RemoteCallback(result -> succeeded.set(true))); 356 waitUntilBgExecutorIdle(); 357 358 assertThat(succeeded.get()).isTrue(); 359 assertThat(mPackageManager.getApplicationEnabledSetting(DLC_PACKAGE_NAME)) 360 .isEqualTo(COMPONENT_ENABLED_STATE_DEFAULT); 361 } 362 363 @Test onUserSwitching_ifNotFinalizedAndDlcDisabled_enables()364 public void onUserSwitching_ifNotFinalizedAndDlcDisabled_enables() throws Exception { 365 // GIVEN device is not finalized and DLC is disabled 366 ShadowBinder.setCallingUserHandle(mSecondaryUser); 367 mPackageManager.setApplicationEnabledSetting( 368 DLC_PACKAGE_NAME, COMPONENT_ENABLED_STATE_DISABLED, /* flags= */ 0); 369 370 // WHEN the service checks finalization 371 mService.onUserSwitching(mSecondaryUser); 372 373 waitUntilBgExecutorIdle(); 374 shadowOf(Looper.getMainLooper()).idle(); 375 376 // THEN DLC is enabled 377 assertThat(mPackageManager.getApplicationEnabledSetting(DLC_PACKAGE_NAME)) 378 .isEqualTo(COMPONENT_ENABLED_STATE_DEFAULT); 379 } 380 381 @Test onUserSwitching_ifFinalizedAndDisabledOnSecondary_doesNothing()382 public void onUserSwitching_ifFinalizedAndDisabledOnSecondary_doesNothing() throws Exception { 383 // GIVEN device is finalized and DLC is disabled on a secondary user 384 ShadowBinder.setCallingUserHandle(mSecondaryUser); 385 mService.setDeviceFinalized(true, new RemoteCallback(result -> {})); 386 waitUntilBgExecutorIdle(); 387 assertThat(mPackageManager.getApplicationEnabledSetting(DLC_PACKAGE_NAME)) 388 .isEqualTo(COMPONENT_ENABLED_STATE_DISABLED); 389 390 // WHEN there is a user switch to a secondary user 391 mService.onUserSwitching(mSecondaryUser); 392 393 waitUntilBgExecutorIdle(); 394 shadowOf(Looper.getMainLooper()).idle(); 395 396 // THEN DLC stays disabled 397 assertThat(mPackageManager.getApplicationEnabledSetting(DLC_PACKAGE_NAME)) 398 .isEqualTo(COMPONENT_ENABLED_STATE_DISABLED); 399 } 400 401 /** 402 * Make the resolve info for the DLC package. 403 */ makeDlcResolveInfo()404 private ResolveInfo makeDlcResolveInfo() { 405 ApplicationInfo appInfo = new ApplicationInfo(); 406 appInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED; 407 appInfo.flags |= ApplicationInfo.FLAG_SYSTEM; 408 ServiceInfo serviceInfo = new ServiceInfo(); 409 serviceInfo.name = DLC_SERVICE_NAME; 410 serviceInfo.packageName = DLC_PACKAGE_NAME; 411 serviceInfo.applicationInfo = appInfo; 412 ResolveInfo resolveInfo = new ResolveInfo(); 413 resolveInfo.serviceInfo = serviceInfo; 414 415 return resolveInfo; 416 } 417 418 /** 419 * Set-up calls to mock the service being connected. 420 */ waitUntilConnected()421 private void waitUntilConnected() { 422 eventually(() -> { 423 shadowOf(Looper.getMainLooper()).idle(); 424 ServiceConnection connection = mShadowApplication.getBoundServiceConnections().get(0); 425 Binder binder = new Binder(); 426 binder.attachInterface(mDeviceLockControllerService, 427 IDeviceLockControllerService.class.getName()); 428 connection.onServiceConnected(new ComponentName(DLC_PACKAGE_NAME, DLC_SERVICE_NAME), 429 binder); 430 }, ONE_SEC_MILLIS); 431 } 432 waitUntilBgExecutorIdle()433 private void waitUntilBgExecutorIdle() throws InterruptedException, ExecutionException { 434 mExecutorService.submit(() -> {}).get(); 435 } 436 } 437