| /* |
| * Copyright (C) 2017 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.server.devicepolicy; |
| |
| import static org.mockito.ArgumentMatchers.anyBoolean; |
| import static org.mockito.ArgumentMatchers.anyInt; |
| import static org.mockito.ArgumentMatchers.eq; |
| import static org.mockito.Mockito.RETURNS_DEEP_STUBS; |
| import static org.mockito.Mockito.mock; |
| import static org.mockito.Mockito.spy; |
| import static org.mockito.Mockito.when; |
| |
| import android.accounts.Account; |
| import android.accounts.AccountManager; |
| import android.app.ActivityManagerInternal; |
| import android.app.AlarmManager; |
| import android.app.IActivityManager; |
| import android.app.NotificationManager; |
| import android.app.backup.IBackupManager; |
| import android.app.usage.UsageStatsManagerInternal; |
| import android.content.BroadcastReceiver; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.content.pm.IPackageManager; |
| import android.content.pm.PackageManager; |
| import android.content.pm.PackageManagerInternal; |
| import android.content.pm.UserInfo; |
| import android.media.IAudioService; |
| import android.net.IIpConnectivityMetrics; |
| import android.net.wifi.WifiManager; |
| import android.os.Handler; |
| import android.os.PowerManager; |
| import android.os.PowerManagerInternal; |
| import android.os.UserHandle; |
| import android.os.UserManager; |
| import android.os.UserManagerInternal; |
| import android.provider.Settings; |
| import android.security.KeyChain; |
| import android.telephony.TelephonyManager; |
| import android.test.mock.MockContentResolver; |
| import android.util.ArrayMap; |
| import android.util.Pair; |
| import android.view.IWindowManager; |
| |
| import com.android.internal.util.test.FakeSettingsProvider; |
| import com.android.internal.widget.LockPatternUtils; |
| import com.android.server.net.NetworkPolicyManagerInternal; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.concurrent.atomic.AtomicReference; |
| |
| /** |
| * System services mocks and some other data that are shared by all contexts during the test. |
| */ |
| public class MockSystemServices { |
| public final File systemUserDataDir; |
| public final EnvironmentForMock environment; |
| public final SystemPropertiesForMock systemProperties; |
| public final UserManager userManager; |
| public final UserManagerInternal userManagerInternal; |
| public final UsageStatsManagerInternal usageStatsManagerInternal; |
| public final NetworkPolicyManagerInternal networkPolicyManagerInternal; |
| public final PackageManagerInternal packageManagerInternal; |
| public final UserManagerForMock userManagerForMock; |
| public final PowerManagerForMock powerManager; |
| public final PowerManagerInternal powerManagerInternal; |
| public final RecoverySystemForMock recoverySystem; |
| public final NotificationManager notificationManager; |
| public final IIpConnectivityMetrics iipConnectivityMetrics; |
| public final IWindowManager iwindowManager; |
| public final IActivityManager iactivityManager; |
| public ActivityManagerInternal activityManagerInternal; |
| public final IPackageManager ipackageManager; |
| public final IBackupManager ibackupManager; |
| public final IAudioService iaudioService; |
| public final LockPatternUtils lockPatternUtils; |
| public final PasswordBlacklist passwordBlacklist; |
| public final StorageManagerForMock storageManager; |
| public final WifiManager wifiManager; |
| public final SettingsForMock settings; |
| public final MockContentResolver contentResolver; |
| public final TelephonyManager telephonyManager; |
| public final AccountManager accountManager; |
| public final AlarmManager alarmManager; |
| public final KeyChain.KeyChainConnection keyChainConnection; |
| /** Note this is a partial mock, not a real mock. */ |
| public final PackageManager packageManager; |
| public final BuildMock buildMock = new BuildMock(); |
| public final File dataDir; |
| |
| public MockSystemServices(Context realContext, String name) { |
| dataDir = new File(realContext.getCacheDir(), name); |
| DpmTestUtils.clearDir(dataDir); |
| |
| environment = mock(EnvironmentForMock.class); |
| systemProperties = mock(SystemPropertiesForMock.class); |
| userManager = mock(UserManager.class); |
| userManagerInternal = mock(UserManagerInternal.class); |
| usageStatsManagerInternal = mock(UsageStatsManagerInternal.class); |
| networkPolicyManagerInternal = mock(NetworkPolicyManagerInternal.class); |
| |
| userManagerForMock = mock(UserManagerForMock.class); |
| packageManagerInternal = mock(PackageManagerInternal.class); |
| powerManager = mock(PowerManagerForMock.class); |
| powerManagerInternal = mock(PowerManagerInternal.class); |
| recoverySystem = mock(RecoverySystemForMock.class); |
| notificationManager = mock(NotificationManager.class); |
| iipConnectivityMetrics = mock(IIpConnectivityMetrics.class); |
| iwindowManager = mock(IWindowManager.class); |
| iactivityManager = mock(IActivityManager.class); |
| activityManagerInternal = mock(ActivityManagerInternal.class); |
| ipackageManager = mock(IPackageManager.class); |
| ibackupManager = mock(IBackupManager.class); |
| iaudioService = mock(IAudioService.class); |
| lockPatternUtils = mock(LockPatternUtils.class); |
| passwordBlacklist = mock(PasswordBlacklist.class); |
| storageManager = mock(StorageManagerForMock.class); |
| wifiManager = mock(WifiManager.class); |
| settings = mock(SettingsForMock.class); |
| telephonyManager = mock(TelephonyManager.class); |
| accountManager = mock(AccountManager.class); |
| alarmManager = mock(AlarmManager.class); |
| keyChainConnection = mock(KeyChain.KeyChainConnection.class, RETURNS_DEEP_STUBS); |
| |
| // Package manager is huge, so we use a partial mock instead. |
| packageManager = spy(realContext.getPackageManager()); |
| |
| contentResolver = new MockContentResolver(); |
| contentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); |
| |
| // Add the system user with a fake profile group already set up (this can happen in the real |
| // world if a managed profile is added and then removed). |
| systemUserDataDir = |
| addUser(UserHandle.USER_SYSTEM, UserInfo.FLAG_PRIMARY, UserHandle.USER_SYSTEM); |
| |
| // System user is always running. |
| setUserRunning(UserHandle.USER_SYSTEM, true); |
| } |
| |
| /** Optional mapping of other user contexts for {@link #createPackageContextAsUser} to return */ |
| private final Map<Pair<UserHandle, String>, Context> userPackageContexts = new ArrayMap<>(); |
| |
| private final ArrayList<UserInfo> mUserInfos = new ArrayList<>(); |
| |
| private final List<BroadcastReceiverRegistration> mBroadcastReceivers = new ArrayList<>(); |
| |
| public void registerReceiver( |
| BroadcastReceiver receiver, IntentFilter filter, Handler scheduler) { |
| mBroadcastReceivers.add(new BroadcastReceiverRegistration(receiver, filter, scheduler)); |
| } |
| |
| public void unregisterReceiver(BroadcastReceiver receiver) { |
| mBroadcastReceivers.removeIf(r -> r.receiver == receiver); |
| } |
| |
| public File addUser(int userId, int flags) { |
| return addUser(userId, flags, UserInfo.NO_PROFILE_GROUP_ID); |
| } |
| |
| public File addUser(int userId, int flags, int profileGroupId) { |
| // Set up (default) UserInfo for CALLER_USER_HANDLE. |
| final UserInfo uh = new UserInfo(userId, "user" + userId, flags); |
| uh.profileGroupId = profileGroupId; |
| when(userManager.getUserInfo(eq(userId))).thenReturn(uh); |
| |
| mUserInfos.add(uh); |
| when(userManager.getUsers()).thenReturn(mUserInfos); |
| when(userManager.getUsers(anyBoolean())).thenReturn(mUserInfos); |
| when(userManager.isUserRunning(eq(new UserHandle(userId)))).thenReturn(true); |
| when(userManager.getUserInfo(anyInt())).thenAnswer( |
| invocation -> { |
| final int userId1 = (int) invocation.getArguments()[0]; |
| return getUserInfo(userId1); |
| } |
| ); |
| when(userManager.getProfileParent(anyInt())).thenAnswer( |
| invocation -> { |
| final int userId1 = (int) invocation.getArguments()[0]; |
| final UserInfo ui = getUserInfo(userId1); |
| return ui == null ? null : getUserInfo(ui.profileGroupId); |
| } |
| ); |
| when(userManager.getProfiles(anyInt())).thenAnswer( |
| invocation -> { |
| final int userId12 = (int) invocation.getArguments()[0]; |
| return getProfiles(userId12); |
| } |
| ); |
| when(userManager.getProfileIdsWithDisabled(anyInt())).thenAnswer( |
| invocation -> { |
| final int userId13 = (int) invocation.getArguments()[0]; |
| List<UserInfo> profiles = getProfiles(userId13); |
| return profiles.stream() |
| .mapToInt(profile -> profile.id) |
| .toArray(); |
| } |
| ); |
| when(accountManager.getAccountsAsUser(anyInt())).thenReturn(new Account[0]); |
| |
| // Create a data directory. |
| final File dir = new File(dataDir, "users/" + userId); |
| DpmTestUtils.clearDir(dir); |
| |
| when(environment.getUserSystemDirectory(eq(userId))).thenReturn(dir); |
| return dir; |
| } |
| |
| public void removeUser(int userId) { |
| for (int i = 0; i < mUserInfos.size(); i++) { |
| if (mUserInfos.get(i).id == userId) { |
| mUserInfos.remove(i); |
| break; |
| } |
| } |
| when(userManager.getUserInfo(eq(userId))).thenReturn(null); |
| |
| when(userManager.isUserRunning(eq(new UserHandle(userId)))).thenReturn(false); |
| } |
| |
| private UserInfo getUserInfo(int userId) { |
| for (final UserInfo ui : mUserInfos) { |
| if (ui.id == userId) { |
| return ui; |
| } |
| } |
| return null; |
| } |
| |
| private List<UserInfo> getProfiles(int userId) { |
| final ArrayList<UserInfo> ret = new ArrayList<>(); |
| UserInfo parent = null; |
| for (final UserInfo ui : mUserInfos) { |
| if (ui.id == userId) { |
| parent = ui; |
| break; |
| } |
| } |
| if (parent == null) { |
| return ret; |
| } |
| for (final UserInfo ui : mUserInfos) { |
| if (ui == parent |
| || ui.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID |
| && ui.profileGroupId == parent.profileGroupId) { |
| ret.add(ui); |
| } |
| } |
| return ret; |
| } |
| |
| /** |
| * Add multiple users at once. They'll all have flag 0. |
| */ |
| public void addUsers(int... userIds) { |
| for (final int userId : userIds) { |
| addUser(userId, 0); |
| } |
| } |
| |
| public void setUserRunning(int userId, boolean isRunning) { |
| when(userManager.isUserRunning(MockUtils.checkUserHandle(userId))) |
| .thenReturn(isRunning); |
| } |
| |
| public void injectBroadcast(Context context, final Intent intent, int userId) { |
| //final int userId = UserHandle.getUserId(binder.getCallingUid()); |
| for (final BroadcastReceiverRegistration receiver : mBroadcastReceivers) { |
| receiver.sendBroadcastIfApplicable(context, userId, intent); |
| } |
| } |
| |
| public void rethrowBackgroundBroadcastExceptions() throws Exception { |
| for (final BroadcastReceiverRegistration receiver : mBroadcastReceivers) { |
| final Exception e = receiver.backgroundException.getAndSet(null); |
| if (e != null) { |
| throw e; |
| } |
| } |
| } |
| |
| public void addPackageContext(UserHandle user, Context context) { |
| if (context.getPackageName() == null) { |
| throw new NullPointerException("getPackageName() == null"); |
| } |
| userPackageContexts.put(new Pair<>(user, context.getPackageName()), context); |
| } |
| |
| public Context createPackageContextAsUser(String packageName, int flags, UserHandle user) |
| throws PackageManager.NameNotFoundException { |
| final Pair<UserHandle, String> key = new Pair<>(user, packageName); |
| if (userPackageContexts.containsKey(key)) { |
| return userPackageContexts.get(key); |
| } |
| throw new UnsupportedOperationException("No package " + packageName + " for user " + user); |
| } |
| |
| |
| public static class EnvironmentForMock { |
| public File getUserSystemDirectory(int userId) { |
| return null; |
| } |
| } |
| |
| public static class BuildMock { |
| public boolean isDebuggable = true; |
| } |
| |
| public static class PowerManagerForMock { |
| public PowerManager.WakeLock newWakeLock(int levelAndFlags, String tag) { |
| return null; |
| } |
| |
| public void goToSleep(long time, int reason, int flags) { |
| } |
| |
| public void reboot(String reason) { |
| } |
| } |
| |
| public static class RecoverySystemForMock { |
| public void rebootWipeUserData(boolean shutdown, String reason, boolean force, |
| boolean wipeEuicc) throws IOException { |
| } |
| } |
| |
| public static class SystemPropertiesForMock { |
| public boolean getBoolean(String key, boolean def) { |
| return false; |
| } |
| |
| public long getLong(String key, long def) { |
| return 0; |
| } |
| |
| public String get(String key, String def) { |
| return null; |
| } |
| |
| public String get(String key) { |
| return null; |
| } |
| |
| public void set(String key, String value) { |
| } |
| } |
| |
| public static class UserManagerForMock { |
| public boolean isSplitSystemUser() { |
| return false; |
| } |
| } |
| |
| public static class SettingsForMock { |
| public int settingsSecureGetIntForUser(String name, int def, int userHandle) { |
| return 0; |
| } |
| |
| public String settingsSecureGetStringForUser(String name, int userHandle) { |
| return null; |
| } |
| |
| public void settingsSecurePutIntForUser(String name, int value, int userHandle) { |
| } |
| |
| public void settingsSecurePutStringForUser(String name, String value, int userHandle) { |
| } |
| |
| public void settingsGlobalPutStringForUser(String name, String value, int userHandle) { |
| } |
| |
| public void settingsSecurePutInt(String name, int value) { |
| } |
| |
| public void settingsGlobalPutInt(String name, int value) { |
| } |
| |
| public void settingsSecurePutString(String name, String value) { |
| } |
| |
| public void settingsGlobalPutString(String name, String value) { |
| } |
| |
| public void settingsSystemPutString(String name, String value) { |
| } |
| |
| public int settingsGlobalGetInt(String name, int value) { |
| return 0; |
| } |
| |
| public String settingsGlobalGetString(String name) { |
| return ""; |
| } |
| |
| public void securityLogSetLoggingEnabledProperty(boolean enabled) { |
| } |
| |
| public boolean securityLogGetLoggingEnabledProperty() { |
| return false; |
| } |
| |
| public boolean securityLogIsLoggingEnabled() { |
| return false; |
| } |
| } |
| |
| public static class StorageManagerForMock { |
| public boolean isFileBasedEncryptionEnabled() { |
| return false; |
| } |
| |
| public boolean isNonDefaultBlockEncrypted() { |
| return false; |
| } |
| |
| public boolean isEncrypted() { |
| return false; |
| } |
| |
| public boolean isEncryptable() { |
| return false; |
| } |
| } |
| |
| // We have to keep track of broadcast receivers registered for a given intent ourselves as the |
| // DPM unit tests mock out the package manager and PackageManager.queryBroadcastReceivers() does |
| // not work. |
| private static class BroadcastReceiverRegistration { |
| public final BroadcastReceiver receiver; |
| public final IntentFilter filter; |
| public final Handler scheduler; |
| |
| // Exceptions thrown in a background thread kill the whole test. Save them instead. |
| public final AtomicReference<Exception> backgroundException = new AtomicReference<>(); |
| |
| public BroadcastReceiverRegistration(BroadcastReceiver receiver, IntentFilter filter, |
| Handler scheduler) { |
| this.receiver = receiver; |
| this.filter = filter; |
| this.scheduler = scheduler; |
| } |
| |
| public void sendBroadcastIfApplicable(Context context, int userId, Intent intent) { |
| final BroadcastReceiver.PendingResult result = new BroadcastReceiver.PendingResult( |
| 0 /* resultCode */, null /* resultData */, null /* resultExtras */, |
| 0 /* type */, false /* ordered */, false /* sticky */, null /* token */, userId, |
| 0 /* flags */); |
| if (filter.match(null, intent, false, "DpmMockContext") > 0) { |
| final Runnable send = () -> { |
| receiver.setPendingResult(result); |
| receiver.onReceive(context, intent); |
| }; |
| if (scheduler != null) { |
| scheduler.post(() -> { |
| try { |
| send.run(); |
| } catch (Exception e) { |
| backgroundException.compareAndSet(null, e); |
| } |
| }); |
| } else { |
| send.run(); |
| } |
| } |
| } |
| } |
| } |