1 /*
2  * Copyright (C) 2021 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.bedstead.nene.devicepolicy;
18 
19 import static android.cts.testapisreflection.TestApisReflectionKt.isRemovingAdmin;
20 import static com.android.bedstead.permissions.CommonPermissions.INTERACT_ACROSS_USERS_FULL;
21 
22 import static com.google.common.truth.Truth.assertThat;
23 
24 import android.app.Activity;
25 import android.app.admin.DeviceAdminReceiver;
26 import android.app.admin.DevicePolicyManager;
27 import android.content.ComponentName;
28 import android.content.Context;
29 import android.content.Intent;
30 
31 import com.android.bedstead.nene.TestApis;
32 import com.android.bedstead.nene.annotations.Experimental;
33 import com.android.bedstead.nene.packages.Package;
34 import com.android.bedstead.nene.users.UserReference;
35 import com.android.bedstead.nene.utils.BlockingBroadcastReceiver;
36 import com.android.bedstead.nene.utils.Poll;
37 import com.android.bedstead.nene.utils.Retry;
38 import com.android.bedstead.permissions.PermissionContext;
39 import com.android.bedstead.testapisreflection.TestApisConstants;
40 
41 import java.time.Duration;
42 import java.util.Objects;
43 
44 /**
45  * A reference to either a Device Owner or a Profile Owner.
46  */
47 public abstract class DevicePolicyController implements AutoCloseable {
48 
49     protected static final String TEST_APP_APP_COMPONENT_FACTORY =
50             "com.android.bedstead.testapp.TestAppAppComponentFactory";
51 
52     protected final UserReference mUser;
53     protected final Package mPackage;
54     protected final ComponentName mComponentName;
55 
DevicePolicyController(UserReference user, Package pkg, ComponentName componentName)56     DevicePolicyController(UserReference user, Package pkg, ComponentName componentName) {
57         if (user == null || pkg == null || componentName == null) {
58             throw new NullPointerException();
59         }
60 
61         mUser = user;
62         mPackage = pkg;
63         mComponentName = componentName;
64     }
65 
66     /**
67      * Get the {@link UserReference} which this device policy controller is installed into.
68      */
user()69     public UserReference user() {
70         return mUser;
71     }
72 
73     /**
74      * Get the {@link Package} of the device policy controller.
75      */
pkg()76     public Package pkg() {
77         return mPackage;
78     }
79 
80     /**
81      * Get the {@link ComponentName} of the {@link DeviceAdminReceiver} for this device policy
82      * controller.
83      */
componentName()84     public ComponentName componentName() {
85         return mComponentName;
86     }
87 
88     /**
89      * Remove this device policy controller.
90      */
remove()91     public abstract void remove();
92 
93     /**
94      * Remove test app.
95      *
96      * Special case for removing TestApp DPCs - this works even when not testOnly
97      * but not on profiles
98      */
removeTestApp()99     protected void removeTestApp() {
100         // Special case for removing TestApp DPCs - this works even when not testOnly
101         Intent intent = new Intent(TestApisConstants.ACTION_DISABLE_SELF);
102         intent.setComponent(new ComponentName(pkg().packageName(),
103                 "com.android.bedstead.testapp.TestAppBroadcastController"));
104         Context context = TestApis.context().androidContextAsUser(mUser);
105 
106         try (PermissionContext p =
107                      TestApis.permissions().withPermission(INTERACT_ACROSS_USERS_FULL)) {
108             // If the profile isn't ready then the broadcast won't be sent and the profile owner
109             // will not be removed. So we can retry until the broadcast has been dealt with.
110             Retry.logic(() -> {
111                 com.android.bedstead.nene.utils.BlockingBroadcastReceiver
112                         b = new BlockingBroadcastReceiver(
113                         TestApis.context().instrumentedContext());
114 
115                 context.sendOrderedBroadcast(
116                         intent, /* receiverPermission= */ null, b, /* scheduler= */
117                         null, /* initialCode= */
118                         Activity.RESULT_CANCELED, /* initialData= */ null, /* initialExtras= */
119                         null);
120 
121                 b.awaitForBroadcastOrFail(Duration.ofSeconds(30).toMillis());
122                 assertThat(b.getResultCode()).isEqualTo(Activity.RESULT_OK);
123             }).timeout(Duration.ofMinutes(5)).runAndWrapException();
124 
125             DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
126 
127             Poll.forValue(() -> isRemovingAdmin(dpm, mComponentName, mUser.id()))
128                     .toNotBeEqualTo(true)
129                     .timeout(Duration.ofMinutes(5))
130                     .errorOnFail()
131                     .await();
132         }
133 
134     }
135 
136     /**
137      * Check if DPC is active
138      */
139     @Experimental
isActive()140     public boolean isActive() {
141         try (PermissionContext p =
142                      TestApis.permissions().withPermission(INTERACT_ACROSS_USERS_FULL)) {
143             DevicePolicyManager devicePolicyManager =
144                     TestApis.context().androidContextAsUser(mUser)
145                             .getSystemService(DevicePolicyManager.class);
146 
147             return devicePolicyManager.isAdminActive(mComponentName);
148         }
149     }
150 
151     /**
152      * Check if DPC has granted the specified policy.
153      */
154     @Experimental
hasGrantedPolicy(int policy)155     public boolean hasGrantedPolicy(int policy) {
156         try (PermissionContext p =
157                      TestApis.permissions().withPermission(INTERACT_ACROSS_USERS_FULL)) {
158             DevicePolicyManager devicePolicyManager =
159                     TestApis.context().androidContextAsUser(mUser)
160                             .getSystemService(DevicePolicyManager.class);
161 
162             return devicePolicyManager.hasGrantedPolicy(mComponentName, policy);
163         }
164     }
165 
166     @Override
hashCode()167     public int hashCode() {
168         return Objects.hashCode(mUser)
169                 + Objects.hashCode(mPackage)
170                 + Objects.hashCode(mComponentName);
171     }
172 
173     /** See {@link #remove}. */
174     @Override
close()175     public void close() {
176         remove();
177     }
178 }
179