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.remotedpc;
18 
19 import static com.android.bedstead.remotedpc.Configuration.REMOTE_DPC_COMPONENT_NAME;
20 import static com.android.compatibility.common.util.FileUtils.readInputStreamFully;
21 
22 import android.app.admin.DevicePolicyManager;
23 import android.content.ComponentName;
24 import android.content.Context;
25 import android.os.UserHandle;
26 
27 import androidx.annotation.Nullable;
28 
29 import com.android.bedstead.nene.TestApis;
30 import com.android.bedstead.nene.devicepolicy.DeviceOwner;
31 import com.android.bedstead.nene.devicepolicy.DevicePolicyController;
32 import com.android.bedstead.nene.devicepolicy.ProfileOwner;
33 import com.android.bedstead.nene.exceptions.NeneException;
34 import com.android.bedstead.nene.users.UserReference;
35 import com.android.bedstead.remotedpc.connected.RemoteDPCBinder;
36 import com.android.bedstead.remotedpc.managers.RemoteDevicePolicyManager;
37 import com.android.bedstead.remotedpc.managers.RemoteDevicePolicyManager_Wrapper;
38 
39 import com.google.android.enterprise.connectedapps.CrossProfileConnector;
40 
41 import java.io.IOException;
42 import java.io.InputStream;
43 
44 /** Entry point to RemoteDPC. */
45 public final class RemoteDpc {
46 
47     private static final TestApis sTestApis = new TestApis();
48     // This must be instrumentation not instrumented to access the resources
49     private static final Context sContext = sTestApis.context().instrumentationContext();
50 
51     public static final ComponentName DPC_COMPONENT_NAME = REMOTE_DPC_COMPONENT_NAME;
52 
53     /**
54      * Get the {@link RemoteDpc} instance for the Device Owner.
55      *
56      * <p>This will return {@code null} if there is no Device Owner or it is not a RemoteDPC app.
57      */
58     @Nullable
deviceOwner()59     public static RemoteDpc deviceOwner() {
60         DeviceOwner deviceOwner = sTestApis.devicePolicy().getDeviceOwner();
61         if (deviceOwner == null || !deviceOwner.componentName().equals(DPC_COMPONENT_NAME)) {
62             return null;
63         }
64 
65         return new RemoteDpc(deviceOwner);
66     }
67 
68     /**
69      * Get the {@link RemoteDpc} instance for the Profile Owner of the current user.
70      *
71      * <p>This will return null if there is no Profile Owner or it is not a RemoteDPC app.
72      */
73     @Nullable
profileOwner()74     public static RemoteDpc profileOwner() {
75         return profileOwner(sTestApis.users().instrumented());
76     }
77 
78     /**
79      * Get the {@link RemoteDpc} instance for the Profile Owner of the given {@code profile}.
80      *
81      * <p>This will return null if there is no Profile Owner or it is not a RemoteDPC app.
82      */
83     @Nullable
profileOwner(UserHandle profile)84     public static RemoteDpc profileOwner(UserHandle profile) {
85         if (profile == null) {
86             throw new NullPointerException();
87         }
88 
89         return profileOwner(sTestApis.users().find(profile));
90     }
91 
92     /**
93      * Get the {@link RemoteDpc} instance for the Profile Owner of the given {@code profile}.
94      *
95      * <p>This will return null if there is no Profile Owner or it is not a RemoteDPC app.
96      */
97     @Nullable
profileOwner(UserReference profile)98     public static RemoteDpc profileOwner(UserReference profile) {
99         if (profile == null) {
100             throw new NullPointerException();
101         }
102 
103         ProfileOwner profileOwner = sTestApis.devicePolicy().getProfileOwner(profile);
104         if (profileOwner == null || !profileOwner.componentName().equals(DPC_COMPONENT_NAME)) {
105             return null;
106         }
107 
108         return new RemoteDpc(profileOwner);
109     }
110 
111     /**
112      * Get the most specific {@link RemoteDpc} instance for the current user.
113      *
114      * <p>If the user has a RemoteDPC Profile Owner, this will refer to that. If it does not but
115      * has a RemoteDPC Device Owner it will refer to that. Otherwise it will return null.
116      */
117     @Nullable
any()118     public static RemoteDpc any() {
119         return any(sTestApis.users().instrumented());
120     }
121 
122     /**
123      * Get the most specific {@link RemoteDpc} instance for the current user.
124      *
125      * <p>If the user has a RemoteDPC Profile Owner, this will refer to that. If it does not but
126      * has a RemoteDPC Device Owner it will refer to that. Otherwise it will return null.
127      */
128     @Nullable
any(UserHandle user)129     public static RemoteDpc any(UserHandle user) {
130         if (user == null) {
131             throw new NullPointerException();
132         }
133 
134         return any(sTestApis.users().find(user));
135     }
136 
137     /**
138      * Get the most specific {@link RemoteDpc} instance for the current user.
139      *
140      * <p>If the user has a RemoteDPC Profile Owner, this will refer to that. If it does not but
141      * has a RemoteDPC Device Owner it will refer to that. Otherwise it will return null.
142      */
143     @Nullable
any(UserReference user)144     public static RemoteDpc any(UserReference user) {
145         RemoteDpc remoteDPC = profileOwner(user);
146         if (remoteDPC != null) {
147             return remoteDPC;
148         }
149         return deviceOwner();
150     }
151 
152     /**
153      * Get the {@link RemoteDpc} controller for the given {@link DevicePolicyController}.
154      */
forDevicePolicyController(DevicePolicyController controller)155     public static RemoteDpc forDevicePolicyController(DevicePolicyController controller) {
156         if (controller == null) {
157             throw new NullPointerException();
158         }
159         if (!controller.componentName().equals(REMOTE_DPC_COMPONENT_NAME)) {
160             throw new IllegalStateException("DevicePolicyController is not a RemoteDPC: "
161                     + controller);
162         }
163 
164         return new RemoteDpc(controller);
165     }
166 
167     /**
168      * Set RemoteDPC as the Device Owner.
169      */
setAsDeviceOwner(UserHandle user)170     public static RemoteDpc setAsDeviceOwner(UserHandle user) {
171         if (user == null) {
172             throw new NullPointerException();
173         }
174         return setAsDeviceOwner(sTestApis.users().find(user));
175     }
176 
177     /**
178      * Set RemoteDPC as the Device Owner.
179      */
setAsDeviceOwner(UserReference user)180     public static RemoteDpc setAsDeviceOwner(UserReference user) {
181         if (user == null) {
182             throw new NullPointerException();
183         }
184 
185         DeviceOwner deviceOwner = sTestApis.devicePolicy().getDeviceOwner();
186         if (deviceOwner != null) {
187             if (deviceOwner.componentName().equals(DPC_COMPONENT_NAME)) {
188                 return new RemoteDpc(deviceOwner); // Already set
189             }
190             deviceOwner.remove();
191         }
192 
193         ensureInstalled(user);
194         return new RemoteDpc(sTestApis.devicePolicy().setDeviceOwner(user,
195                 REMOTE_DPC_COMPONENT_NAME));
196     }
197 
198     /**
199      * Set RemoteDPC as the Profile Owner.
200      */
setAsProfileOwner(UserHandle user)201     public static RemoteDpc setAsProfileOwner(UserHandle user) {
202         if (user == null) {
203             throw new NullPointerException();
204         }
205         return setAsProfileOwner(sTestApis.users().find(user));
206     }
207 
208     /**
209      * Set RemoteDPC as the Profile Owner.
210      */
setAsProfileOwner(UserReference user)211     public static RemoteDpc setAsProfileOwner(UserReference user) {
212         if (user == null) {
213             throw new NullPointerException();
214         }
215 
216         ProfileOwner profileOwner = sTestApis.devicePolicy().getProfileOwner(user);
217         if (profileOwner != null) {
218             if (profileOwner.componentName().equals(DPC_COMPONENT_NAME)) {
219                 return new RemoteDpc(profileOwner); // Already set
220             }
221             profileOwner.remove();
222         }
223 
224         ensureInstalled(user);
225         return new RemoteDpc(sTestApis.devicePolicy().setProfileOwner(user,
226                 REMOTE_DPC_COMPONENT_NAME));
227     }
228 
ensureInstalled(UserReference user)229     private static void ensureInstalled(UserReference user) {
230         sTestApis.packages().install(user, apkBytes());
231     }
232 
apkBytes()233     private static byte[] apkBytes() {
234         int apkId = sContext.getResources().getIdentifier(
235                 "raw/RemoteDPC_DPC", /* defType= */ null, sContext.getPackageName());
236         try (InputStream inputStream =
237                      sContext.getResources().openRawResource(apkId)) {
238             return readInputStreamFully(inputStream);
239         } catch (IOException e) {
240             throw new NeneException("Error when reading RemoteDPC bytes", e);
241         }
242     }
243 
244     private final DevicePolicyController mDevicePolicyController;
245     private final CrossProfileConnector mConnector;
246 
RemoteDpc(DevicePolicyController devicePolicyController)247     private RemoteDpc(DevicePolicyController devicePolicyController) {
248         if (devicePolicyController == null) {
249             throw new NullPointerException();
250         }
251         mDevicePolicyController = devicePolicyController;
252         mConnector = CrossProfileConnector.builder(sTestApis.context().instrumentedContext())
253                 .setBinder(new RemoteDPCBinder(this))
254                 .build();
255     }
256 
257     /**
258      * Get the {@link DevicePolicyController} for this instance of RemoteDPC.
259      */
devicePolicyController()260     public DevicePolicyController devicePolicyController() {
261         return mDevicePolicyController;
262     }
263 
264     /**
265      * Remove RemoteDPC as Device Owner or Profile Owner and uninstall the APK from the user.
266      */
remove()267     public void remove() {
268         mDevicePolicyController.remove();
269         sTestApis.packages().find(REMOTE_DPC_COMPONENT_NAME.getPackageName())
270                 .uninstall(mDevicePolicyController.user());
271     }
272 
273     @Override
hashCode()274     public int hashCode() {
275         return mDevicePolicyController.hashCode();
276     }
277 
278     @Override
equals(Object obj)279     public boolean equals(Object obj) {
280         if (!(obj instanceof RemoteDpc)) {
281             return false;
282         }
283 
284         RemoteDpc other = (RemoteDpc) obj;
285         return other.mDevicePolicyController.equals(mDevicePolicyController);
286     }
287 
288     /**
289      * Get a {@link RemoteDevicePolicyManager} to make calls to {@link DevicePolicyManager} using
290      * this RemoteDPC.
291      */
devicePolicyManager()292     public RemoteDevicePolicyManager devicePolicyManager() {
293         return new RemoteDevicePolicyManager_Wrapper(mConnector);
294     }
295 }
296