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.testapp; 18 19 import android.content.BroadcastReceiver; 20 import android.content.IntentFilter; 21 22 import com.android.bedstead.nene.TestApis; 23 import com.android.bedstead.nene.exceptions.NeneException; 24 import com.android.bedstead.nene.packages.ProcessReference; 25 import com.android.bedstead.nene.users.UserReference; 26 27 import com.google.android.enterprise.connectedapps.ConnectionListener; 28 import com.google.android.enterprise.connectedapps.CrossProfileConnector; 29 import com.google.android.enterprise.connectedapps.exceptions.UnavailableProfileException; 30 31 import java.util.HashMap; 32 import java.util.Map; 33 import java.util.UUID; 34 35 import javax.annotation.Nullable; 36 37 /** 38 * A reference to a specific instance of a {@link TestApp} on a given user. 39 * 40 * <p>The user may not exist, or the test app may not be installed on the user. 41 */ 42 public final class TestAppInstanceReference implements AutoCloseable, ConnectionListener { 43 44 private static final TestApis sTestApis = new TestApis(); 45 46 private final TestApp mTestApp; 47 private final UserReference mUser; 48 private final CrossProfileConnector mConnector; 49 private final Map<IntentFilter, Long> mRegisteredBroadcastReceivers = new HashMap<>(); 50 private boolean mKeepAliveManually = false; 51 private final ProfileTestAppController mTestAppController; 52 private final TestAppActivities mTestAppActivities; 53 TestAppInstanceReference(TestApp testApp, UserReference user)54 TestAppInstanceReference(TestApp testApp, UserReference user) { 55 mTestApp = testApp; 56 mUser = user; 57 mConnector = CrossProfileConnector.builder(sTestApis.context().instrumentedContext()) 58 .setBinder(new TestAppBinder(this)) 59 .build(); 60 mConnector.registerConnectionListener(this); 61 mTestAppController = 62 ProfileTestAppController.create(mConnector); 63 mTestAppActivities = TestAppActivities.create(this); 64 } 65 connector()66 CrossProfileConnector connector() { 67 return mConnector; 68 } 69 70 /** 71 * Access activities on the test app. 72 */ activities()73 public TestAppActivities activities() { 74 return mTestAppActivities; 75 } 76 77 /** 78 * The {@link TestApp} this instance refers to. 79 */ testApp()80 public TestApp testApp() { 81 return mTestApp; 82 } 83 84 /** 85 * The {@link UserReference} this instance refers to. 86 */ user()87 public UserReference user() { 88 return mUser; 89 } 90 91 /** 92 * Uninstall the {@link TestApp} from the user referenced by 93 * this {@link TestAppInstanceReference}. 94 */ uninstall()95 public void uninstall() { 96 mTestApp.uninstall(mUser); 97 } 98 99 /** 100 * Register a {@link BroadcastReceiver} for a given {@link IntentFilter}. 101 * 102 * <p>A new {@link BroadcastReceiver} instance will be created for each {@link IntentFilter}. 103 * 104 * <p>Note that {@link IntentFilter} does not override {@code equals} and one broadcast receiver 105 * will be registered for each instance of {@link IntentFilter} regardless of the content of the 106 * {@link IntentFilter}. 107 * 108 * <p>As registered receivers are only active while the application is open, calling this method 109 * will have the same effect as calling {@link #keepAlive()}. 110 */ registerReceiver(IntentFilter intentFilter)111 public void registerReceiver(IntentFilter intentFilter) { 112 if (mRegisteredBroadcastReceivers.containsKey(intentFilter)) { 113 return; 114 } 115 116 long receiverId = UUID.randomUUID().getMostSignificantBits(); 117 registerReceiver(intentFilter, receiverId); 118 keepAlive(/* manualKeepAlive= */ false); 119 } 120 registerReceiver(IntentFilter intentFilter, long receiverId)121 private void registerReceiver(IntentFilter intentFilter, long receiverId) { 122 try { 123 mConnector.connect(); 124 mTestAppController.other().registerReceiver(receiverId, intentFilter); 125 mRegisteredBroadcastReceivers.put(intentFilter, receiverId); 126 } catch (UnavailableProfileException e) { 127 throw new IllegalStateException("Could not connect to test app", e); 128 } finally { 129 mConnector.stopManualConnectionManagement(); 130 } 131 } 132 133 /** 134 * Unregister the receiver 135 * @param intentFilter 136 */ unregisterReceiver(IntentFilter intentFilter)137 public TestAppInstanceReference unregisterReceiver(IntentFilter intentFilter) { 138 if (!mRegisteredBroadcastReceivers.containsKey(intentFilter)) { 139 return this; 140 } 141 142 long receiverId = mRegisteredBroadcastReceivers.remove(intentFilter); 143 144 try { 145 mConnector.connect(); 146 mTestAppController.other().unregisterReceiver(receiverId); 147 mRegisteredBroadcastReceivers.put(intentFilter, receiverId); 148 } catch (UnavailableProfileException e) { 149 throw new IllegalStateException("Could not connect to test app", e); 150 } finally { 151 mConnector.stopManualConnectionManagement(); 152 } 153 154 if (mRegisteredBroadcastReceivers.isEmpty() && !mKeepAliveManually) { 155 stopKeepAlive(); 156 } 157 158 return this; 159 } 160 161 /** 162 * Starts keeping the test app process alive. 163 * 164 * <p>This ensures that it will receive broadcasts using registered broadcast receivers. 165 * 166 * @see {@link #stopKeepAlive()}. 167 */ keepAlive()168 public TestAppInstanceReference keepAlive() { 169 keepAlive(/* manualKeepAlive=*/ true); 170 return this; 171 } 172 173 /** 174 * Starts keep alive mode and marks it as manual so that it won't be automatically ended if 175 * the last broadcast receiver is unregistered. 176 */ keepAlive(boolean manualKeepAlive)177 private void keepAlive(boolean manualKeepAlive) { 178 mKeepAliveManually = manualKeepAlive; 179 try { 180 connector().connect(); 181 } catch (UnavailableProfileException e) { 182 throw new IllegalStateException("Could not connect to test app. Is it installed?", e); 183 } 184 } 185 186 /** 187 * Stops keeping the target app alive. 188 * 189 * <p>This will not kill the app immediately. To do that see {@link #stop()}. 190 */ stopKeepAlive()191 public TestAppInstanceReference stopKeepAlive() { 192 mKeepAliveManually = false; 193 connector().stopManualConnectionManagement(); 194 return this; 195 } 196 197 /** 198 * Immediately force stops the app. 199 * 200 * <p>This will also stop keeping the target app alive (see {@link #stopKeepAlive()}. 201 */ stop()202 public TestAppInstanceReference stop() { 203 stopKeepAlive(); 204 205 ProcessReference process = mTestApp.reference().runningProcess(mUser); 206 if (process != null) { 207 try { 208 process.kill(); 209 } catch (NeneException e) { 210 throw new NeneException("Error killing process... process is " + process(), e); 211 } 212 } 213 214 return this; 215 } 216 217 /** 218 * Gets the {@link ProcessReference} of the app, if any. 219 */ 220 @Nullable process()221 public ProcessReference process() { 222 return mTestApp.reference().runningProcess(mUser); 223 } 224 225 @Override close()226 public void close() { 227 stopKeepAlive(); 228 uninstall(); 229 } 230 231 @Override connectionChanged()232 public void connectionChanged() { 233 if (mConnector.isConnected()) { 234 // re-register broadcast receivers when re-connected 235 for (Map.Entry<IntentFilter, Long> entry : mRegisteredBroadcastReceivers.entrySet()) { 236 registerReceiver(entry.getKey(), entry.getValue()); 237 } 238 } 239 } 240 } 241