utils to properly suspend and wakeup
* usage of utils is documented in README
Change-Id: If857b08b123e4b2e82a06ccc35213f22ab52e0cd
diff --git a/uiautomator/utils/SleepUtils/AlarmService/Android.mk b/uiautomator/utils/SleepUtils/AlarmService/Android.mk
new file mode 100644
index 0000000..9022f03
--- /dev/null
+++ b/uiautomator/utils/SleepUtils/AlarmService/Android.mk
@@ -0,0 +1,26 @@
+#
+# Copyright (C) 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES += \
+ src/com/android/testing/alarmservice/Alarm.aidl
+LOCAL_PACKAGE_NAME := SleepUtilsAlarmService
+LOCAL_SDK_VERSION := 7
+include $(BUILD_PACKAGE)
diff --git a/uiautomator/utils/SleepUtils/AlarmService/AndroidManifest.xml b/uiautomator/utils/SleepUtils/AlarmService/AndroidManifest.xml
new file mode 100644
index 0000000..1b6de39
--- /dev/null
+++ b/uiautomator/utils/SleepUtils/AlarmService/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project Licensed under the
+ Apache License, Version 2.0 (the "License"); you may not use this file except
+ in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software distributed
+ under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
+ OR CONDITIONS OF ANY KIND, either express or implied. See the License for
+ the specific language governing permissions and limitations under the License. -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.testing.alarmservice" >
+
+ <uses-sdk android:minSdkVersion="7" />
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
+
+ <application android:label="Sleep Utils Alarm Service">
+ <service android:name=".AlarmService"
+ android:label="Sleep Utils Alarm Service"
+ android:exported="true"
+ android:enabled="true">
+ <intent-filter>
+ <action android:name="com.android.testing.ALARM_SERVICE" />
+ </intent-filter>
+ </service>
+ <receiver android:name=".WakeUpCall">
+ <intent-filter>
+ <action android:name="com.android.testing.alarmservice.WAKEUP" />
+ </intent-filter>
+ </receiver>
+ </application>
+</manifest>
diff --git a/uiautomator/utils/SleepUtils/AlarmService/src/com/android/testing/alarmservice/Alarm.aidl b/uiautomator/utils/SleepUtils/AlarmService/src/com/android/testing/alarmservice/Alarm.aidl
new file mode 100644
index 0000000..62a8c48
--- /dev/null
+++ b/uiautomator/utils/SleepUtils/AlarmService/src/com/android/testing/alarmservice/Alarm.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.testing.alarmservice;
+
+interface Alarm {
+ int prepare();
+ int setAlarmAndWait(long timeoutMills);
+ int done();
+}
\ No newline at end of file
diff --git a/uiautomator/utils/SleepUtils/AlarmService/src/com/android/testing/alarmservice/AlarmImpl.java b/uiautomator/utils/SleepUtils/AlarmService/src/com/android/testing/alarmservice/AlarmImpl.java
new file mode 100644
index 0000000..122d55d
--- /dev/null
+++ b/uiautomator/utils/SleepUtils/AlarmService/src/com/android/testing/alarmservice/AlarmImpl.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.testing.alarmservice;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Log;
+
+import com.android.testing.alarmservice.Alarm.Stub;
+
+public class AlarmImpl extends Stub {
+
+ private static final String LOG_TAG = AlarmImpl.class.getSimpleName();
+
+ private Context mContext;
+
+ public AlarmImpl(Context context) {
+ super();
+ mContext = context;
+ }
+
+ @Override
+ public int prepare() throws RemoteException {
+ WakeUpController.getController().getWakeLock().acquire();
+ Log.d(LOG_TAG, "AlarmService prepared, wake lock acquired");
+ return 0;
+ }
+
+ @Override
+ public int setAlarmAndWait(long timeoutMills) throws RemoteException {
+ // calculate when device should be waken up
+ long atTime = SystemClock.elapsedRealtime() + timeoutMills;
+ AlarmManager am = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+ Intent wakupIntent = new Intent(WakeUpCall.WAKEUP_CALL);
+ PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, wakupIntent, 0);
+ // set alarm, which will be delivered in form of the wakeupIntent
+ am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, atTime, pi);
+ Log.d(LOG_TAG, String.format("Alarm set: %d, giving up wake lock", atTime));
+ Object lock = WakeUpController.getController().getWakeSync();
+ // release wakelock and wait for the lock to be poked from the broadcast receiver
+ WakeUpController.getController().getWakeLock().release();
+ // does not really matter if device enters suspend before we start waiting on lock
+ synchronized (lock) {
+ try {
+ lock.wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ Log.d(LOG_TAG, String.format("Alarm triggered, done waiting"));
+ return 0;
+ }
+
+ @Override
+ public int done() throws RemoteException {
+ WakeUpController.getController().getWakeLock().release();
+ return 0;
+ }
+
+}
diff --git a/uiautomator/utils/SleepUtils/AlarmService/src/com/android/testing/alarmservice/AlarmService.java b/uiautomator/utils/SleepUtils/AlarmService/src/com/android/testing/alarmservice/AlarmService.java
new file mode 100644
index 0000000..576a1cf
--- /dev/null
+++ b/uiautomator/utils/SleepUtils/AlarmService/src/com/android/testing/alarmservice/AlarmService.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.testing.alarmservice;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+
+public class AlarmService extends Service {
+
+ private AlarmImpl mAlarmImpl = null;
+ static Context sContext;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ sContext = this;
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return getAlarmImpl();
+ }
+
+ private AlarmImpl getAlarmImpl() {
+ if (mAlarmImpl == null) {
+ mAlarmImpl = new AlarmImpl(this);
+ }
+ return mAlarmImpl;
+ }
+
+ @Override
+ public void onDestroy() {
+ sContext = null;
+ super.onDestroy();
+ }
+}
diff --git a/uiautomator/utils/SleepUtils/AlarmService/src/com/android/testing/alarmservice/WakeUpCall.java b/uiautomator/utils/SleepUtils/AlarmService/src/com/android/testing/alarmservice/WakeUpCall.java
new file mode 100644
index 0000000..f4bb4db
--- /dev/null
+++ b/uiautomator/utils/SleepUtils/AlarmService/src/com/android/testing/alarmservice/WakeUpCall.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.testing.alarmservice;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+/**
+ * The receiver for the alarm we set
+ *
+ */
+public class WakeUpCall extends BroadcastReceiver {
+
+ public static final String WAKEUP_CALL = "com.android.testing.alarmservice.WAKEUP";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // we acquire wakelock without release because user is supposed to manually release it
+ WakeUpController.getController().getWakeLock().acquire();
+ Object lock = WakeUpController.getController().getWakeSync();
+ synchronized (lock) {
+ // poke the lock so the service side can be woken from waiting on the lock
+ lock.notifyAll();
+ }
+ }
+
+}
diff --git a/uiautomator/utils/SleepUtils/AlarmService/src/com/android/testing/alarmservice/WakeUpController.java b/uiautomator/utils/SleepUtils/AlarmService/src/com/android/testing/alarmservice/WakeUpController.java
new file mode 100644
index 0000000..478371f
--- /dev/null
+++ b/uiautomator/utils/SleepUtils/AlarmService/src/com/android/testing/alarmservice/WakeUpController.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.testing.alarmservice;
+
+import android.content.Context;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.util.Log;
+
+/**
+ * A singleton used for controlling and sharing of states/wakelocks
+ *
+ */
+public class WakeUpController {
+
+ private static final String LOG_TAG = WakeUpController.class.getName();
+ private static WakeUpController mController = null;
+ private WakeLock mWakeLock = null;
+ private Object mWakeSync = new Object();
+
+ private WakeUpController() {
+ Log.i(LOG_TAG, "Created instance: 0x" + Integer.toHexString(this.hashCode()));
+ }
+
+ public static synchronized WakeUpController getController() {
+ if (mController == null) {
+ mController = new WakeUpController();
+ }
+ return mController;
+ }
+
+ public WakeLock getWakeLock() {
+ if (mWakeLock == null) {
+ PowerManager pm =
+ (PowerManager) AlarmService.sContext.getSystemService(Context.POWER_SERVICE);
+ mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "testing-alarmservice");
+ Log.i(LOG_TAG, "Create wakelock: 0x" + Integer.toHexString(mWakeLock.hashCode()));
+ }
+ return mWakeLock;
+ }
+
+ public Object getWakeSync() {
+ return mWakeSync;
+ }
+}
diff --git a/uiautomator/utils/SleepUtils/Android.mk b/uiautomator/utils/SleepUtils/Android.mk
new file mode 100644
index 0000000..0e65e22
--- /dev/null
+++ b/uiautomator/utils/SleepUtils/Android.mk
@@ -0,0 +1,2 @@
+LOCAL_PATH:= $(call my-dir)
+include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/uiautomator/utils/SleepUtils/README b/uiautomator/utils/SleepUtils/README
new file mode 100644
index 0000000..bfe07da
--- /dev/null
+++ b/uiautomator/utils/SleepUtils/README
@@ -0,0 +1,23 @@
+This folder contains utils to properly perform timed suspend and wakeup.
+
+AlarmService - a service that client can bind to and perform:
+1) holding wakelock (singleton to this service)
+2) setting alarm for a specified period and releasing the wakelock; service
+ call will block until alarm has been triggered and the wakelock is held
+3) releasing the wakelock
+
+SleepHelper - a self instrumentation meant as a convenient way to trigger
+the service functions from command line. Corresponding to service function
+above, supported operations are:
+1) holding wakelock
+am instrument -w -e command prepare \
+ com.android.testing.sleephelper/.SetAlarm
+
+2) setting alarm and wait til triggered
+am instrument -w -e command set_wait \
+ -e param <time in ms> com.android.testing.sleephelper/.SetAlarm
+Note: for the function to work properly, "-w" parameter is required
+
+3) releasing wakelock
+am instrument -w -e command done \
+ com.android.testing.sleephelper/.SetAlarm
diff --git a/uiautomator/utils/SleepUtils/SleepHelper/Android.mk b/uiautomator/utils/SleepUtils/SleepHelper/Android.mk
new file mode 100644
index 0000000..f8267fd
--- /dev/null
+++ b/uiautomator/utils/SleepUtils/SleepHelper/Android.mk
@@ -0,0 +1,29 @@
+#
+# Copyright (C) 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+# Only compile source java files in this apk.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES += \
+ ../AlarmService/src/com/android/testing/alarmservice/Alarm.aidl
+LOCAL_SDK_VERSION := 7
+LOCAL_PACKAGE_NAME := SleepUtilsSleepHelper
+
+include $(BUILD_PACKAGE)
diff --git a/uiautomator/utils/SleepUtils/SleepHelper/AndroidManifest.xml b/uiautomator/utils/SleepUtils/SleepHelper/AndroidManifest.xml
new file mode 100644
index 0000000..0f1d491
--- /dev/null
+++ b/uiautomator/utils/SleepUtils/SleepHelper/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project Licensed under the
+ Apache License, Version 2.0 (the "License"); you may not use this file except
+ in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software distributed
+ under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
+ OR CONDITIONS OF ANY KIND, either express or implied. See the License for
+ the specific language governing permissions and limitations under the License. -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.testing.sleephelper">
+
+ <uses-sdk android:minSdkVersion="7" />
+ <instrumentation android:label="Sleep Helper"
+ android:name="com.android.testing.sleephelper.SetAlarm"
+ android:targetPackage="com.android.testing.sleephelper" />
+
+ <application android:label="Sleep Utils Sleep Helper">
+ <uses-library android:name="android.test.runner" />
+ </application>
+</manifest>
diff --git a/uiautomator/utils/SleepUtils/SleepHelper/src/com/android/testing/sleephelper/SetAlarm.java b/uiautomator/utils/SleepUtils/SleepHelper/src/com/android/testing/sleephelper/SetAlarm.java
new file mode 100644
index 0000000..b558741
--- /dev/null
+++ b/uiautomator/utils/SleepUtils/SleepHelper/src/com/android/testing/sleephelper/SetAlarm.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.testing.sleephelper;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.Debug;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.testing.alarmservice.Alarm;
+
+public class SetAlarm extends Instrumentation {
+
+ private static final String COMMAND = "command";
+ private static final String PARAM = "param";
+ private static final String CMD_PREPARE = "prepare";
+ private static final String CMD_SET = "set_wait";
+ private static final String CMD_DONE = "done";
+ private static final String SERVICE_ACTION = "com.android.testing.ALARM_SERVICE";
+ private static final String SERVICE_PKG = "com.android.testing.alarmservice";
+ private static final String LOG_TAG = SetAlarm.class.getSimpleName();
+
+ private Alarm mAlarmService = null;
+ private Bundle mArgs = null;
+ private String mCommand = null;
+ private Intent mServceIntent = new Intent(SERVICE_ACTION).setPackage(SERVICE_PKG);
+
+ private ServiceConnection mConn = new ServiceConnection() {
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ Log.d(LOG_TAG, "Service disconnected.");
+ mAlarmService = null;
+ errorFinish("service disconnected");
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ Log.d(LOG_TAG, "Service connected.");
+ mAlarmService = Alarm.Stub.asInterface(service);
+ handleCommands();
+ }
+ };
+
+
+ private void handleCommands() {
+ if (CMD_PREPARE.equals(mCommand)) {
+ callPrepare();
+ } else if (CMD_SET.equals(mCommand)) {
+ String paramString = mArgs.getString(PARAM);
+ if (paramString == null) {
+ errorFinish("argument expected for alarm time");
+ }
+ long timeout = -1;
+ try {
+ timeout = Long.parseLong(paramString);
+ } catch (NumberFormatException nfe) {
+ errorFinish("a number argument is expected");
+ }
+ callSetAndWait(timeout);
+ } else if (CMD_DONE.equals(mCommand)) {
+ callDone();
+ } else {
+ errorFinish("Unrecognized command: " + mCommand);
+ }
+ finish(Activity.RESULT_OK, new Bundle());
+ }
+
+ @Override
+ public void onCreate(Bundle arguments) {
+ super.onCreate(arguments);
+ mCommand = arguments.getString(COMMAND);
+ if ("true".equals(arguments.getString("debug"))) {
+ Debug.waitForDebugger();
+ }
+ if (mCommand == null) {
+ errorFinish("No command specified");
+ }
+ mArgs = arguments;
+ connectToAlarmService();
+ }
+
+ private void errorFinish(String msg) {
+ Bundle ret = new Bundle();
+ ret.putString("error", msg);
+ finish(Activity.RESULT_CANCELED, ret);
+ }
+
+ private void connectToAlarmService() {
+ // start the service with an intent, this ensures the service keeps running after unbind
+ ComponentName cn = getContext().startService(mServceIntent);
+ if (cn == null) {
+ errorFinish("failed to start service");
+ }
+ if (!getContext().bindService(mServceIntent, mConn, Context.BIND_AUTO_CREATE)) {
+ errorFinish("failed to bind service");
+ }
+ }
+
+ private void callPrepare() {
+ try {
+ mAlarmService.prepare();
+ } catch (RemoteException e) {
+ errorFinish("RemoteExeption in prepare()");
+ } finally {
+ getContext().unbindService(mConn);
+ }
+ }
+
+ private void callDone() {
+ try {
+ mAlarmService.done();
+ } catch (RemoteException e) {
+ errorFinish("RemoteExeption in prepare()");
+ } finally {
+ getContext().unbindService(mConn);
+ }
+ // explicitly stop the service (started in prepare()) so that the service is now free
+ // to be reclaimed
+ getContext().stopService(mServceIntent);
+ }
+
+ private void callSetAndWait(long timeoutMills) {
+ try {
+ mAlarmService.setAlarmAndWait(timeoutMills);
+ } catch (RemoteException e) {
+ errorFinish("RemoteExeption in setAlarmAndWait()");
+ } finally {
+ getContext().unbindService(mConn);
+ }
+ }
+}