1 /* 2 * Copyright (C) 2017 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 android.app.cts.android.app.cts.tools; 18 19 import android.content.ComponentName; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.ServiceConnection; 23 import android.os.IBinder; 24 import android.os.SystemClock; 25 import android.util.Log; 26 27 /** 28 * Helper for binding to a service and monitoring the state of that service. 29 */ 30 public final class ServiceConnectionHandler implements ServiceConnection { 31 static final String TAG = "ServiceConnectionHandler"; 32 33 final Context mContext; 34 final Intent mIntent; 35 final long mDefaultWaitTime; 36 boolean mMonitoring; 37 boolean mBound; 38 IBinder mService; 39 40 final ServiceConnection mMainBinding = new ServiceConnection() { 41 @Override 42 public void onServiceConnected(ComponentName name, IBinder service) { 43 } 44 45 @Override 46 public void onServiceDisconnected(ComponentName name) { 47 } 48 }; 49 ServiceConnectionHandler(Context context, Intent intent)50 public ServiceConnectionHandler(Context context, Intent intent) { 51 this(context, intent, 5*1000); 52 } 53 ServiceConnectionHandler(Context context, Intent intent, long defaultWaitTime)54 public ServiceConnectionHandler(Context context, Intent intent, long defaultWaitTime) { 55 mContext = context; 56 mIntent = intent; 57 mDefaultWaitTime = defaultWaitTime; 58 } 59 startMonitoring()60 public void startMonitoring() { 61 synchronized (this) { 62 if (mMonitoring) { 63 throw new IllegalStateException("Already monitoring"); 64 } 65 if (!mContext.bindService(mIntent, this, Context.BIND_WAIVE_PRIORITY 66 | Context.BIND_ALLOW_OOM_MANAGEMENT)) { 67 throw new IllegalStateException("Failed to bind " + mIntent); 68 } 69 mMonitoring = true; 70 mService = null; 71 } 72 } 73 waitForConnect()74 public void waitForConnect() { 75 waitForConnect(mDefaultWaitTime); 76 } 77 waitForConnect(long timeout)78 public void waitForConnect(long timeout) { 79 final long endTime = SystemClock.uptimeMillis() + timeout; 80 81 synchronized (this) { 82 while (mService == null) { 83 final long now = SystemClock.uptimeMillis(); 84 if (now >= endTime) { 85 throw new IllegalStateException("Timed out binding to " + mIntent); 86 } 87 try { 88 wait(endTime - now); 89 } catch (InterruptedException e) { 90 } 91 } 92 } 93 } 94 getServiceIBinder()95 public IBinder getServiceIBinder() { 96 return mService; 97 } 98 waitForDisconnect()99 public void waitForDisconnect() { 100 waitForDisconnect(mDefaultWaitTime); 101 } 102 waitForDisconnect(long timeout)103 public void waitForDisconnect(long timeout) { 104 final long endTime = SystemClock.uptimeMillis() + timeout; 105 106 synchronized (this) { 107 while (mService != null) { 108 final long now = SystemClock.uptimeMillis(); 109 if (now >= endTime) { 110 throw new IllegalStateException("Timed out unbinding from " + mIntent); 111 } 112 try { 113 wait(endTime - now); 114 } catch (InterruptedException e) { 115 } 116 } 117 } 118 } 119 stopMonitoringIfNeeded()120 public void stopMonitoringIfNeeded() { 121 synchronized (this) { 122 if (mMonitoring) { 123 stopMonitoring(); 124 } 125 } 126 } 127 stopMonitoring()128 public void stopMonitoring() { 129 synchronized (this) { 130 if (!mMonitoring) { 131 throw new IllegalStateException("Not monitoring"); 132 } 133 mContext.unbindService(this); 134 mMonitoring = false; 135 } 136 } 137 bind()138 public void bind() { 139 bind(mDefaultWaitTime); 140 } 141 bind(long timeout)142 public void bind(long timeout) { 143 synchronized (this) { 144 if (mBound) { 145 throw new IllegalStateException("Already bound"); 146 } 147 // Here's the trick: the first binding allows us to to see the service come 148 // up and go down but doesn't actually cause it to run or impact process management. 149 // The second binding actually brings it up. 150 startMonitoring(); 151 if (!mContext.bindService(mIntent, mMainBinding, Context.BIND_AUTO_CREATE)) { 152 throw new IllegalStateException("Failed to bind " + mIntent); 153 } 154 mBound = true; 155 waitForConnect(timeout); 156 } 157 } 158 unbind()159 public void unbind() { 160 unbind(mDefaultWaitTime); 161 } 162 unbind(long timeout)163 public void unbind(long timeout) { 164 synchronized (this) { 165 if (!mBound) { 166 throw new IllegalStateException("Not bound"); 167 } 168 // This allows the service to go down. We maintain the second binding to be 169 // able to see the connection go away which is what we want to wait on. 170 mContext.unbindService(mMainBinding); 171 mBound = false; 172 173 try { 174 waitForDisconnect(timeout); 175 } finally { 176 stopMonitoring(); 177 } 178 } 179 } 180 cleanup()181 public void cleanup() { 182 cleanup(mDefaultWaitTime); 183 } 184 cleanup(long timeout)185 public void cleanup(long timeout) { 186 synchronized (this) { 187 if (mBound) { 188 unbind(timeout); 189 } else if (mMonitoring) { 190 stopMonitoring(); 191 } 192 } 193 } 194 195 @Override onServiceConnected(ComponentName name, IBinder service)196 public void onServiceConnected(ComponentName name, IBinder service) { 197 synchronized (this) { 198 mService = service; 199 notifyAll(); 200 } 201 } 202 203 @Override onServiceDisconnected(ComponentName name)204 public void onServiceDisconnected(ComponentName name) { 205 synchronized (this) { 206 mService = null; 207 notifyAll(); 208 } 209 } 210 211 @Override onBindingDied(ComponentName name)212 public void onBindingDied(ComponentName name) { 213 synchronized (this) { 214 // We want to remain connected to this service. 215 if (mMonitoring) { 216 Log.d(TAG, "Disconnected but monitoring, unbinding " + this + "..."); 217 mContext.unbindService(this); 218 Log.d(TAG, "...and rebinding"); 219 mContext.bindService(mIntent, this, Context.BIND_WAIVE_PRIORITY); 220 } 221 } 222 } 223 } 224 225