1/*
2 * Copyright (C) 2015 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
17package com.android.server.devicepolicy;
18
19import static org.mockito.ArgumentMatchers.anyBoolean;
20import static org.mockito.ArgumentMatchers.anyLong;
21import static org.mockito.ArgumentMatchers.anyString;
22import static org.mockito.Matchers.anyInt;
23import static org.mockito.Matchers.eq;
24import static org.mockito.Mockito.doReturn;
25import static org.mockito.Mockito.when;
26
27import android.app.admin.DevicePolicyManager;
28import android.content.ComponentName;
29import android.content.Context;
30import android.content.Intent;
31import android.content.pm.ActivityInfo;
32import android.content.pm.ApplicationInfo;
33import android.content.pm.PackageInfo;
34import android.content.pm.PackageManager;
35import android.content.pm.ResolveInfo;
36import android.os.UserHandle;
37import android.test.AndroidTestCase;
38
39import java.util.List;
40
41public abstract class DpmTestBase extends AndroidTestCase {
42    public static final String TAG = "DpmTest";
43
44    protected Context mRealTestContext;
45    protected DpmMockContext mMockContext;
46    private MockSystemServices mServices;
47
48    public ComponentName admin1;
49    public ComponentName admin2;
50    public ComponentName admin3;
51    public ComponentName adminAnotherPackage;
52    public ComponentName adminNoPerm;
53
54    @Override
55    protected void setUp() throws Exception {
56        super.setUp();
57
58        mRealTestContext = super.getContext();
59
60        mServices = new MockSystemServices(mRealTestContext, "test-data");
61        mMockContext = new DpmMockContext(mServices, mRealTestContext);
62
63        admin1 = new ComponentName(mRealTestContext, DummyDeviceAdmins.Admin1.class);
64        admin2 = new ComponentName(mRealTestContext, DummyDeviceAdmins.Admin2.class);
65        admin3 = new ComponentName(mRealTestContext, DummyDeviceAdmins.Admin3.class);
66        adminAnotherPackage = new ComponentName(DpmMockContext.ANOTHER_PACKAGE_NAME,
67                "whatever.random.class");
68        adminNoPerm = new ComponentName(mRealTestContext, DummyDeviceAdmins.AdminNoPerm.class);
69        mockSystemPropertiesToReturnDefault();
70    }
71
72    @Override
73    public DpmMockContext getContext() {
74        return mMockContext;
75    }
76
77    public MockSystemServices getServices() {
78        return mServices;
79    }
80
81    protected interface DpmRunnable {
82        void run(DevicePolicyManager dpm) throws Exception;
83    }
84
85    /**
86     * Simulate an RPC from {@param caller} to the service context ({@link #mMockContext}).
87     *
88     * The caller sees its own context. The server also sees its own separate context, with the
89     * appropriate calling UID and calling permissions fields already set up.
90     */
91    protected void runAsCaller(DpmMockContext caller, DevicePolicyManagerServiceTestable dpms,
92            DpmRunnable action) {
93        final DpmMockContext serviceContext = mMockContext;
94
95        // Save calling UID and PID before clearing identity so we don't run into aliasing issues.
96        final int callingUid = caller.binder.callingUid;
97        final int callingPid = caller.binder.callingPid;
98
99        final long origId = serviceContext.binder.clearCallingIdentity();
100        try {
101            serviceContext.binder.callingUid = callingUid;
102            serviceContext.binder.callingPid = callingPid;
103            serviceContext.binder.callingPermissions.put(callingUid, caller.permissions);
104            action.run(new DevicePolicyManagerTestable(caller, dpms));
105        } catch (Exception e) {
106            throw new AssertionError(e);
107        } finally {
108            serviceContext.binder.restoreCallingIdentity(origId);
109        }
110    }
111
112    private void markPackageAsInstalled(String packageName, ApplicationInfo ai, int userId)
113            throws Exception {
114        final PackageInfo pi = DpmTestUtils.cloneParcelable(
115                mRealTestContext.getPackageManager().getPackageInfo(
116                        mRealTestContext.getPackageName(), 0));
117        assertTrue(pi.applicationInfo.flags != 0);
118
119        if (ai != null) {
120            pi.applicationInfo = ai;
121        }
122
123        doReturn(pi).when(mServices.ipackageManager).getPackageInfo(
124                eq(packageName),
125                eq(0),
126                eq(userId));
127
128        doReturn(ai.uid).when(mServices.packageManager).getPackageUidAsUser(
129                eq(packageName),
130                eq(userId));
131    }
132
133    protected void setUpPackageManagerForAdmin(ComponentName admin, int packageUid)
134            throws Exception {
135        setUpPackageManagerForAdmin(admin, packageUid,
136                /* enabledSetting =*/ null, /* appTargetSdk = */ null);
137    }
138
139    protected void setUpPackageManagerForAdmin(ComponentName admin, int packageUid,
140            int enabledSetting) throws Exception {
141        setUpPackageManagerForAdmin(admin, packageUid, enabledSetting, /* appTargetSdk = */ null);
142    }
143
144    protected void setUpPackageManagerForAdmin(ComponentName admin, int packageUid,
145            Integer enabledSetting, Integer appTargetSdk) throws Exception {
146        setUpPackageManagerForFakeAdmin(admin, packageUid, enabledSetting, appTargetSdk,
147                admin);
148    }
149
150    protected void setUpPackageManagerForFakeAdmin(ComponentName admin, int packageUid,
151            ComponentName copyFromAdmin)
152            throws Exception {
153        setUpPackageManagerForFakeAdmin(admin, packageUid,
154                /* enabledSetting =*/ null, /* appTargetSdk = */ null, copyFromAdmin);
155    }
156
157    /**
158     * Set up a component in the mock package manager to be an active admin.
159     *
160     * @param admin ComponentName that's visible to the test code, which doesn't have to exist.
161     * @param copyFromAdmin package information for {@code admin} will be built based on this
162     *    component's information.
163     */
164    private void setUpPackageManagerForFakeAdmin(ComponentName admin, int packageUid,
165            Integer enabledSetting, Integer appTargetSdk, ComponentName copyFromAdmin)
166            throws Exception {
167
168        // Set up getApplicationInfo().
169
170        final ApplicationInfo ai = DpmTestUtils.cloneParcelable(
171                mRealTestContext.getPackageManager().getApplicationInfo(
172                        copyFromAdmin.getPackageName(),
173                        PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS));
174        ai.enabledSetting = enabledSetting == null
175                ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
176                : enabledSetting;
177        if (appTargetSdk != null) {
178            ai.targetSdkVersion = appTargetSdk;
179        }
180        ai.uid = packageUid;
181        ai.packageName = admin.getPackageName();
182        ai.name = admin.getClassName();
183
184        doReturn(ai).when(mServices.ipackageManager).getApplicationInfo(
185                eq(admin.getPackageName()),
186                anyInt(),
187                eq(UserHandle.getUserId(packageUid)));
188
189        // Set up queryBroadcastReceivers().
190
191        final Intent resolveIntent = new Intent();
192        resolveIntent.setComponent(copyFromAdmin);
193        final List<ResolveInfo> realResolveInfo =
194                mRealTestContext.getPackageManager().queryBroadcastReceivers(
195                        resolveIntent,
196                        PackageManager.GET_META_DATA);
197        assertNotNull(realResolveInfo);
198        assertEquals(1, realResolveInfo.size());
199
200        // We need to change AI, so set a clone.
201        realResolveInfo.set(0, DpmTestUtils.cloneParcelable(realResolveInfo.get(0)));
202
203        // We need to rewrite the UID in the activity info.
204        final ActivityInfo aci = realResolveInfo.get(0).activityInfo;
205        aci.applicationInfo = ai;
206        aci.packageName = admin.getPackageName();
207        aci.name = admin.getClassName();
208
209        // Note we don't set up queryBroadcastReceivers.  We don't use it in DPMS.
210
211        doReturn(aci).when(mServices.ipackageManager).getReceiverInfo(
212                eq(admin),
213                anyInt(),
214                eq(UserHandle.getUserId(packageUid)));
215
216        doReturn(new String[] {admin.getPackageName()}).when(mServices.ipackageManager)
217            .getPackagesForUid(eq(packageUid));
218        // Set up getPackageInfo().
219        markPackageAsInstalled(admin.getPackageName(), ai, UserHandle.getUserId(packageUid));
220    }
221
222    /**
223     * By default, system properties are mocked to return default value. Override the mock if you
224     * want a specific value.
225     */
226    private void mockSystemPropertiesToReturnDefault() {
227        when(getServices().systemProperties.get(
228                anyString(), anyString())).thenAnswer(
229                invocation -> invocation.getArguments()[1]
230        );
231
232        when(getServices().systemProperties.getBoolean(
233                anyString(), anyBoolean())).thenAnswer(
234                invocation -> invocation.getArguments()[1]
235        );
236
237        when(getServices().systemProperties.getLong(
238                anyString(), anyLong())).thenAnswer(
239                invocation -> invocation.getArguments()[1]
240        );
241    }
242}
243