1 /* 2 * Copyright (C) 2014 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.cts.verifier.sensors.helpers; 18 19 import com.android.cts.verifier.os.TimeoutResetActivity; 20 import com.android.cts.verifier.sensors.base.BaseSensorTestActivity; 21 import com.android.cts.verifier.sensors.base.ISensorTestStateContainer; 22 23 import android.app.Activity; 24 import android.app.admin.DeviceAdminInfo; 25 import android.app.admin.DevicePolicyManager; 26 import android.content.BroadcastReceiver; 27 import android.content.ComponentName; 28 import android.content.Context; 29 import android.content.Intent; 30 import android.content.IntentFilter; 31 import android.content.pm.PackageManager; 32 import android.os.PowerManager; 33 import android.text.TextUtils; 34 import android.util.Log; 35 36 import java.util.concurrent.CountDownLatch; 37 38 /** 39 * A class that provides functionality to manipulate the state of the device's screen. 40 * 41 * The implementation uses a simple state machine with 3 states: keep-screen-off, keep-screen-on, 42 * and a free-state where the class does not affect the system's state. 43 * 44 * The list of transitions and their handlers are: 45 * keep-screen-on --(turnScreenOff)--> keep-screen-off 46 * keep-screen-on --(releaseScreenOn)--> free-state 47 * 48 * keep-screen-off --(turnScreenOn)--> keep-screen-on 49 * keep-screen-off --(wakeUpScreen)--> free-state 50 * 51 * free-state --(turnScreenOff)--> keep-screen-off 52 * free-state --(turnScreenOn)--> keep-screen-on 53 * 54 * NOTES: 55 * - the operator still can turn on/off the screen by pressing the power button 56 * - this class must be used by a single client, that can manage the state of the instance, likely 57 * - in a single-threaded environment 58 */ 59 public class SensorTestScreenManipulator { 60 private static final String TAG = SensorTestScreenManipulator.class.getSimpleName(); 61 62 private final Activity mActivity; 63 private final DevicePolicyManager mDevicePolicyManager; 64 private final ComponentName mComponentName; 65 private final PowerManager.WakeLock mWakeUpScreenWakeLock; 66 private final PowerManager.WakeLock mKeepScreenOnWakeLock; 67 68 private InternalBroadcastReceiver mBroadcastReceiver; 69 private boolean mTurnOffScreenOnPowerDisconnected; 70 71 SensorTestScreenManipulator(Activity activity)72 public SensorTestScreenManipulator(Activity activity) { 73 mActivity = activity; 74 mComponentName = SensorDeviceAdminReceiver.getComponentName(activity); 75 mDevicePolicyManager = 76 (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE); 77 78 int levelAndFlags = PowerManager.FULL_WAKE_LOCK 79 | PowerManager.ON_AFTER_RELEASE 80 | PowerManager.ACQUIRE_CAUSES_WAKEUP; 81 PowerManager powerManager = (PowerManager) activity.getSystemService(Context.POWER_SERVICE); 82 mWakeUpScreenWakeLock = powerManager.newWakeLock(levelAndFlags, "SensorTestWakeUpScreen"); 83 mWakeUpScreenWakeLock.setReferenceCounted(false); 84 mKeepScreenOnWakeLock = powerManager.newWakeLock(levelAndFlags, "SensorTestKeepScreenOn"); 85 mKeepScreenOnWakeLock.setReferenceCounted(false); 86 } 87 88 /** 89 * Initializes the current instance. 90 * Initialization should usually happen inside {@link BaseSensorTestActivity#activitySetUp}. 91 * 92 * NOTE: Initialization will bring up an Activity to let the user activate the Device Admin, 93 * this method will block until the user completes the operation. 94 */ initialize(ISensorTestStateContainer stateContainer)95 public synchronized void initialize(ISensorTestStateContainer stateContainer) 96 throws InterruptedException { 97 if (hasDeviceAdminFeature() && !isDeviceAdminInitialized()) { 98 Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN); 99 intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, mComponentName); 100 int resultCode = stateContainer.executeActivity(intent); 101 if (resultCode != Activity.RESULT_OK) { 102 throw new IllegalStateException( 103 "Test cannot execute without Activating the Device Administrator."); 104 } 105 } 106 107 if (mBroadcastReceiver == null) { 108 mBroadcastReceiver = new InternalBroadcastReceiver(); 109 IntentFilter intentFilter = new IntentFilter(); 110 intentFilter.addAction(Intent.ACTION_POWER_DISCONNECTED); 111 mActivity.registerReceiver(mBroadcastReceiver, intentFilter); 112 } 113 } 114 115 /** 116 * Closes the current instance. 117 * This operation should usually happen inside {@link BaseSensorTestActivity#activityCleanUp}. 118 */ close()119 public synchronized void close() { 120 if (mBroadcastReceiver != null) { 121 mActivity.unregisterReceiver(mBroadcastReceiver); 122 mBroadcastReceiver = null; 123 } 124 } 125 126 /** 127 * Instruct the device to turn off the screen immediately. 128 */ turnScreenOff()129 public synchronized void turnScreenOff() { 130 ensureDeviceAdminInitialized(); 131 132 final CountDownLatch screenOffSignal = new CountDownLatch(1); 133 BroadcastReceiver screenOffBroadcastReceiver = new BroadcastReceiver() { 134 @Override 135 public void onReceive(Context context, Intent intent) { 136 mActivity.unregisterReceiver(this); 137 screenOffSignal.countDown(); 138 } 139 }; 140 mActivity.registerReceiver( 141 screenOffBroadcastReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF)); 142 143 releaseScreenOn(); 144 if (hasDeviceAdminFeature()) { 145 mDevicePolicyManager.lockNow(); 146 } else { 147 TimeoutResetActivity.turnOffScreen(mActivity); 148 } 149 150 try { 151 screenOffSignal.await(); 152 } catch (InterruptedException e) { 153 Log.wtf(TAG, "error waiting for screen off signal", e); 154 } 155 } 156 157 /** 158 * Instruct the device to wake up the screen immediately, the screen will remain on for a bit, 159 * but the system might turn the screen off in the near future. 160 */ wakeUpScreen()161 public synchronized void wakeUpScreen() { 162 mWakeUpScreenWakeLock.acquire(); 163 // release right away, the screen still remains on for a bit, but not indefinitely 164 mWakeUpScreenWakeLock.release(); 165 } 166 167 /** 168 * Instructs the device to turn on the screen immediately. 169 * 170 * The screen will remain on until the client invokes {@link #releaseScreenOn()}, or the user 171 * presses the device's power button. 172 */ turnScreenOn()173 public synchronized void turnScreenOn() { 174 if (mKeepScreenOnWakeLock.isHeld()) { 175 // recover from cases when we could get out of sync, this can happen because the user 176 // can press the power button, and other wake-locks can prevent intents to be received 177 mKeepScreenOnWakeLock.release(); 178 } 179 mKeepScreenOnWakeLock.acquire(); 180 } 181 182 /** 183 * Indicates that the client does not require the screen to remain on anymore. 184 * 185 * See {@link #turnScreenOn()} for more information. 186 */ releaseScreenOn()187 public synchronized void releaseScreenOn() { 188 if (!mKeepScreenOnWakeLock.isHeld()) { 189 return; 190 } 191 mKeepScreenOnWakeLock.release(); 192 } 193 194 /** 195 * Queues a request to turn off the screen off when the device has been disconnected from a 196 * power source (usually upon USB disconnected). 197 * 198 * (It is useful for Sensor Power Tests, as the Power Monitor usually detaches itself from the 199 * device before beginning to sample data). 200 */ turnScreenOffOnNextPowerDisconnect()201 public synchronized void turnScreenOffOnNextPowerDisconnect() { 202 ensureDeviceAdminInitialized(); 203 mTurnOffScreenOnPowerDisconnected = true; 204 } 205 ensureDeviceAdminInitialized()206 private void ensureDeviceAdminInitialized() throws IllegalStateException { 207 if (hasDeviceAdminFeature() && !isDeviceAdminInitialized()) { 208 throw new IllegalStateException("Component must be initialized before it can be used."); 209 } 210 } 211 isDeviceAdminInitialized()212 private boolean isDeviceAdminInitialized() { 213 if (!mDevicePolicyManager.isAdminActive(mComponentName)) { 214 return false; 215 } 216 return mDevicePolicyManager 217 .hasGrantedPolicy(mComponentName, DeviceAdminInfo.USES_POLICY_FORCE_LOCK); 218 } 219 hasDeviceAdminFeature()220 private boolean hasDeviceAdminFeature() { 221 return mActivity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN); 222 } 223 224 private class InternalBroadcastReceiver extends BroadcastReceiver { 225 @Override onReceive(Context context, Intent intent)226 public void onReceive(Context context, Intent intent) { 227 String action = intent.getAction(); 228 229 if (TextUtils.equals(action, Intent.ACTION_POWER_DISCONNECTED)) { 230 if (mTurnOffScreenOnPowerDisconnected) { 231 turnScreenOff(); 232 // reset the flag after it has triggered once, we try to avoid cases when the test 233 // might leave the receiver enabled after itself, 234 // this approach still provides a way to multiplex one time requests 235 mTurnOffScreenOnPowerDisconnected = false; 236 } 237 } 238 } 239 } 240 } 241