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