Merge "media: allow test to continue waiting for complete if there's new encoder output" into lmp-mr1-dev
diff --git a/CtsTestCaseList.mk b/CtsTestCaseList.mk
index 16f9ec1..e8e7485 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -34,6 +34,7 @@
CtsSplitApp_arm64-v8a \
CtsSplitApp_mips64 \
CtsSplitApp_mips \
+ CtsSplitAppDiffRevision \
CtsSplitAppDiffVersion \
CtsSplitAppDiffCert \
CtsSplitAppFeature \
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index ae3f5bb..d3f77e3 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -500,6 +500,10 @@
android:label="@string/nfc_hce_payment_dynamic_aids_emulator"
android:configChanges="keyboardHidden|orientation|screenSize" />
+ <activity android:name=".nfc.hce.LargeNumAidsEmulatorActivity"
+ android:label="@string/nfc_hce_large_num_aids_emulator"
+ android:configChanges="keyboardHidden|orientation|screenSize" />
+
<activity android:name=".nfc.hce.PrefixPaymentEmulatorActivity"
android:label="@string/nfc_hce_payment_prefix_aids_emulator"
android:configChanges="keyboardHidden|orientation|screenSize" />
@@ -635,6 +639,15 @@
</intent-filter>
<meta-data android:name="android.nfc.cardemulation.host_apdu_service" android:resource="@xml/access_prefix_aid_list"/>
</service>
+ <service android:name=".nfc.hce.LargeNumAidsService" android:exported="true"
+ android:permission="android.permission.BIND_NFC_SERVICE"
+ android:enabled="false">
+ <intent-filter>
+ <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ <meta-data android:name="android.nfc.cardemulation.host_apdu_service" android:resource="@xml/payment_aid_list_1"/>
+ </service>
<!-- Service used for Camera ITS tests -->
<service android:name=".camera.its.ItsService" >
@@ -1262,8 +1275,7 @@
</activity>
- <!-- TODO: enable when the test can be executed without leaving marks -->
- <!-- activity android:name=".managedprovisioning.ByodFlowTestActivity"
+ <activity android:name=".managedprovisioning.ByodFlowTestActivity"
android:launchMode="singleTask"
android:label="@string/provisioning_byod">
<intent-filter>
@@ -1276,12 +1288,13 @@
</intent-filter>
<meta-data android:name="test_category" android:value="@string/test_category_managed_provisioning" />
<meta-data android:name="test_required_features" android:value="android.software.managed_users:android.software.device_admin" />
- </activity-->
+ </activity>
<activity android:name=".managedprovisioning.ByodHelperActivity">
<intent-filter>
<action android:name="com.android.cts.verifier.managedprovisioning.BYOD_QUERY" />
<action android:name="com.android.cts.verifier.managedprovisioning.BYOD_REMOVE" />
+ <action android:name="com.android.cts.verifier.managedprovisioning.BYOD_INSTALL_APK" />
<category android:name="android.intent.category.DEFAULT"></category>
</intent-filter>
</activity>
@@ -1300,6 +1313,14 @@
</intent-filter>
</activity>
+ <activity android:name=".managedprovisioning.WorkNotificationTestActivity">
+ <intent-filter>
+ <action android:name="com.android.cts.verifier.managedprovisioning.WORK_NOTIFICATION" />
+ <action android:name="com.android.cts.verifier.managedprovisioning.CLEAR_WORK_NOTIFICATION" />
+ <category android:name="android.intent.category.DEFAULT"></category>
+ </intent-filter>
+ </activity>
+
<receiver android:name=".managedprovisioning.DeviceAdminTestReceiver"
android:label="@string/provisioning_byod_device_admin"
android:permission="android.permission.BIND_DEVICE_ADMIN">
@@ -1386,6 +1407,15 @@
android:value="android.software.live_tv" />
</activity>
+ <activity android:name=".screenpinning.ScreenPinningTestActivity"
+ android:label="@string/screen_pinning_test">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.cts.intent.category.MANUAL_TEST" />
+ </intent-filter>
+ <meta-data android:name="test_category" android:value="@string/test_category_other" />
+ </activity>
+
<activity android:name=".tv.MockTvInputSettingsActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
diff --git a/apps/CtsVerifier/assets/scripts/execute_power_tests.py b/apps/CtsVerifier/assets/scripts/execute_power_tests.py
index d1c2dac..d07cb36 100755
--- a/apps/CtsVerifier/assets/scripts/execute_power_tests.py
+++ b/apps/CtsVerifier/assets/scripts/execute_power_tests.py
@@ -25,57 +25,108 @@
import pkgutil
import threading
import Queue
+import traceback
+import math
+import bisect
+from bisect import bisect_left
-# queue to signal thread to exit
-signal_exit_q = Queue.Queue()
-signal_abort = Queue.Queue()
+"""
+scipy, numpy and matplotlib are python packages that can be installed
+from: http://www.scipy.org/
+
+"""
+import scipy
+import matplotlib.pyplot as plt
# let this script know about the power monitor implementations
sys.path = [os.path.basename(__file__)] + sys.path
-available_monitors = [name for _, name, _ in pkgutil.iter_modules(
- [os.path.join(os.path.dirname(__file__),'power_monitors')]) if not name.startswith('_')]
+available_monitors = [
+ name
+ for _, name, _ in pkgutil.iter_modules(
+ [os.path.join(os.path.dirname(__file__), "power_monitors")])
+ if not name.startswith("_")]
-APK = os.path.join( os.path.dirname(__file__), '..', "CtsVerifier.apk")
+APK = os.path.join(os.path.dirname(__file__), "..", "CtsVerifier.apk")
FLAGS = flags.FLAGS
-# whether to use a strict delay to ensure screen is off, or attempt to use power measurements
-USE_STRICT_DELAY = False
-if USE_STRICT_DELAY:
- DELAY_SCREEN_OFF = 30.0
-else:
- DELAY_SCREEN_OFF = 2.0
+# DELAY_SCREEN_OFF is the number of seconds to wait for baseline state
+DELAY_SCREEN_OFF = 20.0
# whether to log data collected to a file for each sensor run:
LOG_DATA_TO_FILE = True
logging.getLogger().setLevel(logging.ERROR)
+
def do_import(name):
"""import a module by name dynamically"""
mod = __import__(name)
- components = name.split('.')
+ components = name.split(".")
for comp in components[1:]:
mod = getattr(mod, comp)
return mod
+class PowerTestException(Exception):
+ """
+ Definition of specialized Exception class for CTS power tests
+ """
+ def __init__(self, message):
+ self._error_message = message
+ def __str__(self):
+ return self._error_message
class PowerTest:
- """Class to run a suite of power tests"""
+ """Class to run a suite of power tests. This has methods for obtaining
+ measurements from the power monitor (through the driver) and then
+ processing it to determine baseline and AP suspend state and
+ measure ampere draw of various sensors.
+ Ctrl+C causes a keyboard interrupt exception which terminates the test."""
# Thresholds for max allowed power usage per sensor tested
- MAX_ACCEL_POWER = 0.08 # Amps
- MAX_MAG_POWER = 0.08 # Amps
- MAX_GYRO_POWER = 0.08 # Amps
- MAX_SIGMO_POWER = 0.08 # Amps
- MAX_STEP_COUNTER_POWER = 0.08 # Amps
- MAX_STEP_DETECTOR_POWER = 0.08 # Amps
+ # TODO: Accel, Mag and Gyro have no maximum power specified in the CDD;
+ # the following numbers are bogus and will be replaced soon by what
+ # the device reports (from Sensor.getPower())
+ MAX_ACCEL_AMPS = 0.08 # Amps
+ MAX_MAG_AMPS = 0.08 # Amps
+ MAX_GYRO_AMPS = 0.08 # Amps
+ MAX_SIGMO_AMPS = 0.08 # Amps
-
- PORT = 0 # any available port
+ # TODO: The following numbers for step counter, etc must be replaced by
+ # the numbers specified in CDD for low-power sensors. The expected current
+ # draw must be computed from the specified power and the voltage used to
+ # power the device (specified from a config file).
+ MAX_STEP_COUNTER_AMPS = 0.08 # Amps
+ MAX_STEP_DETECTOR_AMPS = 0.08 # Amps
+ # The variable EXPECTED_AMPS_VARIATION_HALF_RANGE denotes the expected
+ # variation of the ampere measurements
+ # around the mean value at baseline state. i.e. we expect most of the
+ # ampere measurements at baseline state to vary around the mean by
+ # between +/- of the number below
+ EXPECTED_AMPS_VARIATION_HALF_RANGE = 0.0005
+ # The variable THRESHOLD_BASELINE_SAMPLES_FRACTION denotes the minimum fraction of samples that must
+ # be in the range of variation defined by EXPECTED_AMPS_VARIATION_HALF_RANGE
+ # around the mean baseline for us to decide that the phone has settled into
+ # its baseline state
+ THRESHOLD_BASELINE_SAMPLES_FRACTION = 0.86
+ # The variable MAX_PERCENTILE_AP_SCREEN_OFF_AMPS denotes the maximum ampere
+ # draw that the device can consume when it has gone to suspend state with
+ # one or more sensors registered and batching samples (screen and AP are
+ # off in this case)
+ MAX_PERCENTILE_AP_SCREEN_OFF_AMPS = 0.030 # Amps
+ # The variable PERCENTILE_MAX_AP_SCREEN_OFF denotes the fraction of ampere
+ # measurements that must be below the specified maximum amperes
+ # MAX_PERCENTILE_AP_SCREEN_OFF_AMPS for us to decide that the phone has
+ # reached suspend state.
+ PERCENTILE_MAX_AP_SCREEN_OFF = 0.95
DOMAIN_NAME = "/android/cts/powertest"
+ # SAMPLE_COUNT_NOMINAL denotes the typical number of measurements of amperes
+ # to collect from the power monitor
SAMPLE_COUNT_NOMINAL = 1000
+ # RATE_NOMINAL denotes the nominal frequency at which ampere measurements
+ # are taken from the monsoon power monitor
RATE_NOMINAL = 100
+ ENABLE_PLOTTING = False
REQUEST_EXTERNAL_STORAGE = "EXTERNAL STORAGE?"
REQUEST_EXIT = "EXIT"
@@ -87,103 +138,157 @@
REQUEST_SCREEN_OFF = "SCREEN OFF"
REQUEST_SHOW_MESSAGE = "MESSAGE %s"
+ NEGATIVE_AMPERE_ERROR_MESSAGE = (
+ "Negative ampere draw measured, possibly due to power "
+ "supply from USB cable. Check the setup of device and power "
+ "monitor to make sure that the device is not connected "
+ "to machine via USB directly. The device should be "
+ "connected to the USB slot in the power monitor. It is okay "
+ "to change the wiring when the test is in progress.")
- def __init__(self):
+
+ def __init__(self, max_baseline_amps):
+ """
+ Args:
+ max_baseline_amps: The maximum value of baseline amperes
+ that we expect the device to consume at baseline state.
+ This can be different between models of phones.
+ """
power_monitors = do_import("power_monitors.%s" % FLAGS.power_monitor)
testid = time.strftime("%d_%m_%Y__%H__%M_%S")
self._power_monitor = power_monitors.Power_Monitor(log_file_id = testid)
+ self._tcp_connect_port = 0 # any available port
print ("Establishing connection to device...")
self.setUsbEnabled(True)
status = self._power_monitor.GetStatus()
self._native_hz = status["sampleRate"] * 1000
+ # the following describes power test being run (i.e on what sensor
+ # and what type of test. This is used for logging.
self._current_test = "None"
- self._external_storage = self.executeOnDevice(PowerTest.REQUEST_EXTERNAL_STORAGE,
- reportErrors=True)
+ self._external_storage = self.executeOnDevice(PowerTest.REQUEST_EXTERNAL_STORAGE)
+ self._max_baseline_amps = max_baseline_amps
def __del__(self):
self.finalize()
def finalize(self):
"""To be called upon termination of host connection to device"""
- if PowerTest.PORT > 0:
- # tell device side to exit connection loop, and remove the forwarding connection
- self.executeOnDevice(PowerTest.REQUEST_EXIT, reportErrors=False)
- self.executeLocal("adb forward --remove tcp:%d" % PowerTest.PORT)
- PowerTest.PORT = 0
+ if self._tcp_connect_port > 0:
+ # tell device side to exit connection loop, and remove the forwarding
+ # connection
+ self.executeOnDevice(PowerTest.REQUEST_EXIT, reportErrors = False)
+ self.executeLocal("adb forward --remove tcp:%d" % self._tcp_connect_port)
+ self._tcp_connect_port = 0
if self._power_monitor:
self._power_monitor.Close()
self._power_monitor = None
- def _send(self, msg, report_errors=True):
+ def _send(self, msg, report_errors = True):
"""Connect to the device, send the given command, and then disconnect"""
- if PowerTest.PORT == 0:
+ if self._tcp_connect_port == 0:
# on first attempt to send a command, connect to device via any open port number,
# forwarding that port to a local socket on the device via adb
logging.debug("Seeking port for communication...")
# discover an open port
dummysocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
dummysocket.bind(("localhost", 0))
- (_, PowerTest.PORT) = dummysocket.getsockname()
+ (_, self._tcp_connect_port) = dummysocket.getsockname()
dummysocket.close()
- assert(PowerTest.PORT > 0)
+ assert(self._tcp_connect_port > 0)
+
status = self.executeLocal("adb forward tcp:%d localabstract:%s" %
- (PowerTest.PORT, PowerTest.DOMAIN_NAME))
- if report_errors:
- self.reportErrorIf(status != 0, msg="Unable to forward requests to client over adb")
- logging.info("Forwarding requests over local port %d" % PowerTest.PORT)
+ (self._tcp_connect_port, PowerTest.DOMAIN_NAME))
+ # If the status !=0, then the host machine is unable to
+ # forward requests to client over adb. Ending the test and logging error message
+ # to the console on the host.
+ self.endTestIfLostConnection(
+ status != 0,
+ "Unable to forward requests to client over adb")
+ logging.info("Forwarding requests over local port %d",
+ self._tcp_connect_port)
link = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
logging.debug("Connecting to device...")
- link.connect (("localhost", PowerTest.PORT))
+ link.connect(("localhost", self._tcp_connect_port))
logging.debug("Connected.")
+ except socket.error as serr:
+ print "Socket connection error: ", serr
+ print "Finalizing and exiting the test"
+ self.endTestIfLostConnection(
+ report_errors,
+ "Unable to communicate with device: connection refused")
except:
- if report_errors:
- self.reportErrorIf(True, msg="Unable to communicate with device: connection refused")
- logging.debug("Sending '%s'" % msg)
+ print "Non socket-related exception at this block in _send(); re-raising now."
+ raise
+ logging.debug("Sending '%s'", msg)
link.sendall(msg)
logging.debug("Getting response...")
response = link.recv(4096)
- logging.debug("Got response '%s'" % response)
+ logging.debug("Got response '%s'", response)
link.close()
return response
def queryDevice(self, query):
"""Post a yes/no query to the device, return True upon successful query, False otherwise"""
- logging.info("Querying device with '%s'" % query)
+ logging.info("Querying device with '%s'", query)
return self._send(query) == "OK"
# TODO: abstract device communication (and string commands) into its own class
- def executeOnDevice(self, cmd , reportErrors=True):
+ def executeOnDevice(self, cmd, reportErrors = True):
"""Execute a (string) command on the remote device"""
- return self._send(cmd , reportErrors)
+ return self._send(cmd, reportErrors)
- def executeLocal(self, cmd, check_status=True):
+ def executeLocal(self, cmd, check_status = True):
"""execute a shell command locally (on the host)"""
from subprocess import call
- status = call(cmd.split(' '))
+ status = call(cmd.split(" "))
if status != 0 and check_status:
- logging.error("Failed to execute \"%s\"" % cmd)
+ logging.error("Failed to execute \"%s\"", cmd)
else:
- logging.debug("Executed \"%s\"" % cmd)
+ logging.debug("Executed \"%s\"", cmd)
return status
- def reportErrorIf(self, condition, msg):
+ def reportErrorRaiseExceptionIf(self, condition, msg):
"""Report an error condition to the device if condition is True.
- Will raise an exception on the device if condition is True"""
+ Will raise an exception on the device if condition is True.
+ Args:
+ condition: If true, this reports error
+ msg: Message related to exception
+ Raises:
+ A PowerTestException encapsulating the message provided in msg
+ """
if condition:
try:
logging.error("Exiting on error: %s" % msg)
- self.executeOnDevice(PowerTest.REQUEST_RAISE % (self._current_test, msg), False)
+ self.executeOnDevice(PowerTest.REQUEST_RAISE % (self._current_test, msg),
+ reportErrors = True)
except:
-
- logging.error("Unable to communicate with device to report error: %s" % msg)
+ logging.error("Unable to communicate with device to report "
+ "error: %s" % msg)
self.finalize()
sys.exit(msg)
- raise Exception(msg)
+ raise PowerTestException(msg)
- def setUsbEnabled(self, enabled, verbose=True):
+ def endTestIfLostConnection(self, lost_connection, error_message):
+ """
+ This function ends the test if lost_connection was true,
+ which indicates that the connection to the device was lost.
+ Args:
+ lost_connection: boolean variable, if True it indicates that
+ connection to device was lost and the test must be terminated.
+ error_message: String to print to the host console before exiting the test
+ (if lost_connection is True)
+ Returns:
+ None.
+ """
+ if lost_connection:
+ logging.error(error_message)
+ self.finalize()
+ sys.exit(error_message)
+
+ def setUsbEnabled(self, enabled, verbose = True):
if enabled:
val = 1
else:
@@ -193,6 +298,7 @@
# Sometimes command won't go through first time, particularly if immediately after a data
# collection, so allow for retries
+ # TODO: Move this retry mechanism to the power monitor driver.
status = self._power_monitor.GetStatus()
while status is None and tries < 5:
tries += 1
@@ -203,59 +309,230 @@
status = self._power_monitor.GetStatus()
if enabled:
- if verbose: print("...USB enabled, waiting for device")
- self.executeLocal ("adb wait-for-device")
- if verbose: print("...device online")
+ if verbose:
+ print("...USB enabled, waiting for device")
+ self.executeLocal("adb wait-for-device")
+ if verbose:
+ print("...device online")
else:
- if verbose: logging.info("...USB disabled")
+ if verbose:
+ logging.info("...USB disabled")
# re-establish port forwarding
- if enabled and PowerTest.PORT > 0:
+ if enabled and self._tcp_connect_port > 0:
status = self.executeLocal("adb forward tcp:%d localabstract:%s" %
- (PowerTest.PORT, PowerTest.DOMAIN_NAME))
- self.reportErrorIf(status != 0, msg="Unable to forward requests to client over adb")
+ (self._tcp_connect_port, PowerTest.DOMAIN_NAME))
+ self.reportErrorRaiseExceptionIf(status != 0, msg = "Unable to forward requests to client over adb")
- def waitForScreenOff(self):
- # disconnect of USB will cause screen to go on, so must wait (1 second more than screen off
- # timeout)
- if USE_STRICT_DELAY:
- time.sleep(DELAY_SCREEN_OFF)
- return
+ def computeBaselineState(self, measurements):
+ """
+ Args:
+ measurements: List of floats containing ampere draw measurements
+ taken from the monsoon power monitor.
+ Must be atleast 100 measurements long
+ Returns:
+ A tuple (isBaseline, mean_current) where isBaseline is a
+ boolean that is True only if the baseline state for the phone is
+ detected. mean_current is an estimate of the average baseline
+ current for the device, which is valid only if baseline state is
+ detected (if not, it is set to -1).
+ """
- # need at least 100 sequential clean low-power measurements to know screen is off
- THRESHOLD_COUNT_LOW_POWER = 100
- CURRENT_LOW_POWER_THRESHOLD = 0.060 # Amps
- TIMEOUT_SCREEN_OFF = 30 # this many tries at most
- count_good = 0
- tries = 0
- print("Waiting for screen off and application processor in suspend mode...")
- while count_good < THRESHOLD_COUNT_LOW_POWER:
- measurements = self.collectMeasurements(THRESHOLD_COUNT_LOW_POWER,
- PowerTest.RATE_NOMINAL,
- ensure_screen_off=False,
- verbose=False)
- count_good = len([m for m in measurements
- if m < CURRENT_LOW_POWER_THRESHOLD])
- tries += 1
- if count_good < THRESHOLD_COUNT_LOW_POWER and measurements:
- print("Current usage: %.2f mAmps. Device is probably not in suspend mode. Waiting..." %
- (1000.0*(sum(measurements)/len(measurements))))
- if tries >= TIMEOUT_SCREEN_OFF:
- # TODO: dump the state of sensor service to identify if there are features using sensors
- self.reportErrorIf(tries>=TIMEOUT_SCREEN_OFF,
- msg="Unable to determine application processor suspend mode status.")
+ # Looks at the measurements to see if it is in baseline state
+ if len(measurements) < 100:
+ print(
+ "Need at least 100 measurements to determine if baseline state has"
+ " been reached")
+ return (False, -1)
+
+ # Assumption: At baseline state, the power profile is Gaussian distributed
+ # with low-variance around the mean current draw.
+ # Ideally we should find the mode from a histogram bin to find an estimated mean.
+ # Assuming here that the median is very close to this value; later we check that the
+ # variance of the samples is low enough to validate baseline.
+ sorted_measurements = sorted(measurements)
+ number_measurements = len(measurements)
+ if not number_measurements % 2:
+ median_measurement = (sorted_measurements[(number_measurements - 1) / 2] +
+ sorted_measurements[(number_measurements + 1) / 2]) / 2
+ else:
+ median_measurement = sorted_measurements[number_measurements / 2]
+
+ # Assume that at baseline state, a large fraction of power measurements
+ # are within +/- EXPECTED_AMPS_VARIATION_HALF_RANGE milliAmperes of
+ # the average baseline current. Find all such measurements in the
+ # sorted measurement vector.
+ left_index = (
+ bisect_left(
+ sorted_measurements,
+ median_measurement -
+ PowerTest.EXPECTED_AMPS_VARIATION_HALF_RANGE))
+ right_index = (
+ bisect_left(
+ sorted_measurements,
+ median_measurement +
+ PowerTest.EXPECTED_AMPS_VARIATION_HALF_RANGE))
+
+ average_baseline_amps = scipy.mean(
+ sorted_measurements[left_index: (right_index - 1)])
+
+ detected_baseline = True
+ # We enforce that a fraction of more than 'THRESHOLD_BASELINE_SAMPLES_FRACTION'
+ # of samples must be within +/- EXPECTED_AMPS_VARIATION_HALF_RANGE
+ # milliAmperes of the mean baseline current, which we have estimated as
+ # the median.
+ if ((right_index - left_index) < PowerTest.THRESHOLD_BASELINE_SAMPLES_FRACTION * len(
+ measurements)):
+ detected_baseline = False
+
+ # We check for the maximum limit of the expected baseline
+ if median_measurement > self._max_baseline_amps:
+ detected_baseline = False
+ if average_baseline_amps < 0:
+ print PowerTest.NEGATIVE_AMPERE_ERROR_MESSAGE
+ detected_baseline = False
+
+ print("%s baseline state" % ("Could detect" if detected_baseline else "Could NOT detect"))
+ print(
+ "median amps = %f, avg amps = %f, fraction of good samples = %f" %
+ (median_measurement, average_baseline_amps,
+ float(right_index - left_index) / len(measurements)))
+ if PowerTest.ENABLE_PLOTTING:
+ plt.plot(measurements)
+ plt.show()
+ print("To continue test, please close the plot window manually.")
+ return (detected_baseline, average_baseline_amps)
+
+ def isApInSuspendState(self, measurements_amps, nominal_max_amps, test_percentile):
+ """
+ This function detects AP suspend and display off state of phone
+ after a sensor has been registered.
+
+ Because the power profile can be very different between sensors and
+ even across builds, it is difficult to specify a tight threshold for
+ mean current draw or mandate that the power measurements must have low
+ variance. We use a criteria that allows for a certain fraction of
+ peaks in power spectrum and checks that test_percentile fraction of
+ measurements must be below the specified value nominal_max_amps
+ Args:
+ measurements_amps: amperes draw measurements from power monitor
+ test_percentile: the fraction of measurements we require to be below
+ a specified amps value
+ nominal_max_amps: the specified value of the max current draw
+ Returns:
+ returns a boolean which is True if and only if the AP suspend and
+ display off state is detected
+ """
+ count_good = len([m for m in measurements_amps if m < nominal_max_amps])
+ count_negative = len([m for m in measurements_amps if m < 0])
+ if count_negative > 0:
+ print PowerTest.NEGATIVE_AMPERE_ERROR_MESSAGE
+ return False;
+ return count_good > test_percentile * len(measurements_amps)
+
+ def getBaselineState(self):
+ """This function first disables all sensors, then collects measurements
+ through the power monitor and continuously evaluates if baseline state
+ is reached. Once baseline state is detected, it returns a tuple with
+ status information. If baseline is not detected in a preset maximum
+ number of trials, it returns as well.
+
+ Returns:
+ Returns a tuple (isBaseline, mean_current) where isBaseline is a
+ boolean that is True only if the baseline state for the phone is
+ detected. mean_current is an estimate of the average baseline current
+ for the device, which is valid only if baseline state is detected
+ (if not, it is set to -1)
+ """
+ self.setPowerOn("ALL", False)
+ self.setUsbEnabled(False)
+ print("Waiting %d seconds for baseline state" % DELAY_SCREEN_OFF)
+ time.sleep(DELAY_SCREEN_OFF)
+
+ MEASUREMENT_DURATION_SECONDS_BASELINE_DETECTION = 5 # seconds
+ NUMBER_MEASUREMENTS_BASELINE_DETECTION = (
+ PowerTest.RATE_NOMINAL *
+ MEASUREMENT_DURATION_SECONDS_BASELINE_DETECTION)
+ NUMBER_MEASUREMENTS_BASELINE_VERIFICATION = (
+ NUMBER_MEASUREMENTS_BASELINE_DETECTION * 5)
+ MAX_TRIALS = 50
+
+ collected_baseline_measurements = False
+
+ for tries in xrange(MAX_TRIALS):
+ print("Trial number %d of %d..." % (tries, MAX_TRIALS))
+ measurements = self.collectMeasurements(
+ NUMBER_MEASUREMENTS_BASELINE_DETECTION, PowerTest.RATE_NOMINAL,
+ verbose = False)
+ if self.computeBaselineState(measurements)[0] is True:
+ collected_baseline_measurements = True
break
- if DELAY_SCREEN_OFF:
- # add additional delay time if necessary
- time.sleep(DELAY_SCREEN_OFF)
- print("...Screen off and device in suspend mode.")
- def collectMeasurements(self, measurementCount, rate , ensure_screen_off=True, verbose=True,
- plot_data = False):
- assert(measurementCount > 0)
- decimate_by = self._native_hz / rate or 1
- if ensure_screen_off:
- self.waitForScreenOff()
- print ("Taking measurements...")
+ if collected_baseline_measurements:
+ print("Verifying baseline state over a longer interval "
+ "in order to double check baseline state")
+ measurements = self.collectMeasurements(
+ NUMBER_MEASUREMENTS_BASELINE_VERIFICATION, PowerTest.RATE_NOMINAL,
+ verbose = False)
+ self.reportErrorRaiseExceptionIf(
+ not measurements, "No background measurements could be taken")
+ retval = self.computeBaselineState(measurements)
+ if retval[0]:
+ print("Verified baseline.")
+ if measurements and LOG_DATA_TO_FILE:
+ with open("/tmp/cts-power-tests-background-data.log", "w") as f:
+ for m in measurements:
+ f.write("%.4f\n" % m)
+ return retval
+ else:
+ return (False, -1)
+
+ def waitForApSuspendMode(self):
+ """This function repeatedly collects measurements until AP suspend and display off
+ mode is detected. After a maximum number of trials, if this state is not reached, it
+ raises an error.
+ Returns:
+ boolean which is True if device was detected to be in suspend state
+ Raises:
+ Power monitor-related exception
+ """
+ print("waitForApSuspendMode(): Sleeping for %d seconds" % DELAY_SCREEN_OFF)
+ time.sleep(DELAY_SCREEN_OFF)
+
+ NUMBER_MEASUREMENTS = 200
+ # Maximum trials for which to collect measurements to get to Ap suspend
+ # state
+ MAX_TRIALS = 50
+
+ got_to_suspend_state = False
+ for count in xrange(MAX_TRIALS):
+ print ("waitForApSuspendMode(): Trial %d of %d" % (count, MAX_TRIALS))
+ measurements = self.collectMeasurements(NUMBER_MEASUREMENTS,
+ PowerTest.RATE_NOMINAL,
+ verbose = False)
+ if self.isApInSuspendState(
+ measurements, PowerTest.MAX_PERCENTILE_AP_SCREEN_OFF_AMPS,
+ PowerTest.PERCENTILE_MAX_AP_SCREEN_OFF):
+ got_to_suspend_state = True
+ break
+ self.reportErrorRaiseExceptionIf(
+ got_to_suspend_state is False,
+ msg = "Unable to determine application processor suspend mode status.")
+ print("Got to AP suspend state")
+ return got_to_suspend_state
+
+ def collectMeasurements(self, measurementCount, rate, verbose = True):
+ """Args:
+ measurementCount: Number of measurements to collect from the power
+ monitor
+ rate: The integer frequency in Hertz at which to collect measurements from
+ the power monitor
+ Returns:
+ A list containing measurements from the power monitor; that has the
+ requested count of the number of measurements at the specified rate
+ """
+ assert (measurementCount > 0)
+ decimate_by = self._native_hz / rate or 1
+
self._power_monitor.StartDataCollection()
sub_measurements = []
measurements = []
@@ -273,47 +550,70 @@
tries = 0
sub_measurements.extend(additional)
while len(sub_measurements) >= decimate_by:
- sub_avg = sum(sub_measurements) / len(sub_measurements)
+ sub_avg = sum(sub_measurements[0:decimate_by]) / decimate_by
measurements.append(sub_avg)
sub_measurements = sub_measurements[decimate_by:]
if verbose:
+ # "\33[1A\33[2K" is a special Linux console control
+ # sequence for moving to the previous line, and
+ # erasing it; and reprinting new text on that
+ # erased line.
sys.stdout.write("\33[1A\33[2K")
- print ("MEASURED[%d]: %f" % (len(measurements),measurements[-1]))
+ print ("MEASURED[%d]: %f" % (len(measurements), measurements[-1]))
finally:
self._power_monitor.StopDataCollection()
- self.reportErrorIf(measurementCount > len(measurements),
- "Unable to collect all requested measurements")
+ self.reportErrorRaiseExceptionIf(measurementCount > len(measurements),
+ "Unable to collect all requested measurements")
return measurements
- def request_user_acknowledgment(self, msg):
+ def requestUserAcknowledgment(self, msg):
"""Post message to user on screen and wait for acknowledgment"""
response = self.executeOnDevice(PowerTest.REQUEST_USER_RESPONSE % msg)
- self.reportErrorIf(response != "OK", "Unable to request user acknowledgment")
+ self.reportErrorRaiseExceptionIf(
+ response != "OK", "Unable to request user acknowledgment")
- def setTestResult (self, testname, condition, msg):
- if condition is False:
- val = "FAIL"
- elif condition is True:
- val = "PASS"
- else:
- val = condition
- print ("Test %s : %s" % (testname, val))
- response = self.executeOnDevice(PowerTest.REQUEST_SET_TEST_RESULT % (testname, val, msg))
- self.reportErrorIf(response != "OK", "Unable to send test status to Verifier")
+ def setTestResult(self, test_name, test_result, test_message):
+ """
+ Reports the result of a test to the device
+ Args:
+ test_name: name of the test
+ test_result: Boolean result of the test (True means Pass)
+ test_message: Relevant message
+ """
+ print ("Test %s : %s" % (test_name, test_result))
+
+ response = (
+ self.executeOnDevice(
+ PowerTest.REQUEST_SET_TEST_RESULT %
+ (test_name, test_result, test_message)))
+ self.reportErrorRaiseExceptionIf(
+ response != "OK", "Unable to send test status to Verifier")
def setPowerOn(self, sensor, powered_on):
response = self.executeOnDevice(PowerTest.REQUEST_SENSOR_SWITCH %
- ({True:"ON", False:"OFF"}[powered_on], sensor))
- self.reportErrorIf(response == "ERR", "Unable to set sensor %s state" % sensor)
- logging.info("Set %s %s" % (sensor, {True:"ON", False:"OFF"}[powered_on]))
+ (("ON" if powered_on else "OFF"), sensor))
+ self.reportErrorRaiseExceptionIf(
+ response == "ERR", "Unable to set sensor %s state" % sensor)
+ logging.info("Set %s %s", sensor, ("ON" if powered_on else "OFF"))
return response
- def runPowerTest(self, sensor, max_power_allowed, user_request = None):
- if not signal_abort.empty():
- sys.exit( signal_abort.get() )
- self._current_test = "%s_Power_Test_While_%s" % (sensor,
- {True:"Under_Motion", False:"Still"}[user_request is not None])
+ def runSensorPowerTest(
+ self, sensor, max_amperes_allowed, baseline_amps, user_request = None):
+ """
+ Runs power test for a specific sensor; i.e. measures the amperes draw
+ of the phone using monsoon, with the specified sensor mregistered
+ and the phone in suspend state; and verifies that the incremental
+ consumed amperes is within expected bounds.
+ Args:
+ sensor: The specified sensor for which to run the power test
+ max_amperes_allowed: Maximum ampere draw of the device with the
+ sensor registered and device in suspend state
+ baseline_amps: The power draw of the device when it is in baseline
+ state (no sensors registered, display off, AP asleep)
+ """
+ self._current_test = ("%s_Power_Test_While_%s" % (
+ sensor, ("Under_Motion" if user_request is not None else "Still")))
try:
print ("\n\n---------------------------------")
if user_request is not None:
@@ -321,248 +621,299 @@
else:
print ("Running power test on %s while device is still." % sensor)
print ("---------------------------------")
- response = self.executeOnDevice(PowerTest.REQUEST_SENSOR_AVAILABILITY % sensor)
+ response = self.executeOnDevice(
+ PowerTest.REQUEST_SENSOR_AVAILABILITY % sensor)
if response == "UNAVAILABLE":
- self.setTestResult(self._current_test, condition="SKIPPED",
- msg="Sensor %s not available on this platform"%sensor)
+ self.setTestResult(
+ self._current_test, test_result = "SKIPPED",
+ test_message = "Sensor %s not available on this platform" % sensor)
self.setPowerOn("ALL", False)
if response == "UNAVAILABLE":
- self.setTestResult(self._current_test, condition="SKIPPED",
- msg="Sensor %s not available on this device"%sensor)
+ self.setTestResult(
+ self._current_test, test_result = "SKIPPED",
+ test_message = "Sensor %s not available on this device" % sensor)
return
-
- self.reportErrorIf(response != "OK", "Unable to set all sensor off")
- if not signal_abort.empty():
- sys.exit( signal_abort.get() )
+ self.reportErrorRaiseExceptionIf(response != "OK", "Unable to set all sensor off")
self.executeOnDevice(PowerTest.REQUEST_SCREEN_OFF)
self.setUsbEnabled(False)
- print("Collecting background measurements...")
- measurements = self.collectMeasurements( PowerTest.SAMPLE_COUNT_NOMINAL,
- PowerTest.RATE_NOMINAL,
- plot_data = True)
- if measurements and LOG_DATA_TO_FILE:
- with open( "/tmp/cts-power-tests-%s-%s-background-data.log"%(sensor,
- {True:"Under_Motion", False:"Still"}[user_request is not None] ),'w') as f:
- for m in measurements:
- f.write( "%.4f\n"%m)
- self.reportErrorIf(not measurements, "No background measurements could be taken")
- backgnd = sum(measurements) / len(measurements)
self.setUsbEnabled(True)
self.setPowerOn(sensor, True)
if user_request is not None:
print("===========================================\n" +
"==> Please follow the instructions presented on the device\n" +
- "==========================================="
- )
- self.request_user_acknowledgment(user_request)
+ "===========================================")
+ self.requestUserAcknowledgment(user_request)
self.executeOnDevice(PowerTest.REQUEST_SCREEN_OFF)
self.setUsbEnabled(False)
- self.reportErrorIf(response != "OK", "Unable to set sensor %s ON" % sensor)
+ self.reportErrorRaiseExceptionIf(
+ response != "OK", "Unable to set sensor %s ON" % sensor)
+
+ self.waitForApSuspendMode()
print ("Collecting sensor %s measurements" % sensor)
measurements = self.collectMeasurements(PowerTest.SAMPLE_COUNT_NOMINAL,
PowerTest.RATE_NOMINAL)
if measurements and LOG_DATA_TO_FILE:
- with open( "/tmp/cts-power-tests-%s-%s-sensor-data.log"%(sensor,
- {True:"Under_Motion", False:"Still"}[user_request is not None] ),'w') as f:
+ with open("/tmp/cts-power-tests-%s-%s-sensor-data.log" % (sensor,
+ ("Under_Motion" if user_request is not None else "Still")), "w") as f:
for m in measurements:
- f.write( "%.4f\n"%m)
+ f.write("%.4f\n" % m)
self.setUsbEnabled(True, verbose = False)
print("Saving raw data files to device...")
self.executeLocal("adb shell mkdir -p %s" % self._external_storage, False)
self.executeLocal("adb push %s %s/." % (f.name, self._external_storage))
self.setUsbEnabled(False, verbose = False)
- self.reportErrorIf(not measurements, "No measurements could be taken for %s" % sensor)
+ self.reportErrorRaiseExceptionIf(
+ not measurements, "No measurements could be taken for %s" % sensor)
avg = sum(measurements) / len(measurements)
- squared = [(m-avg)*(m-avg) for m in measurements]
+ squared = [(m - avg) * (m - avg) for m in measurements]
- import math
- stddev = math.sqrt(sum(squared)/len(squared))
- current_diff = avg - backgnd
+ stddev = math.sqrt(sum(squared) / len(squared))
+ current_diff = avg - baseline_amps
self.setUsbEnabled(True)
max_power = max(measurements) - avg
- if current_diff <= max_power_allowed:
+ if current_diff <= max_amperes_allowed:
# TODO: fail the test of background > current
- message = ("Draw is within limits. Current:%f Background:%f Measured: %f Stddev: %f Peak: %f")%\
- ( current_diff*1000.0, backgnd*1000.0, avg*1000.0, stddev*1000.0, max_power*1000.0)
+ message = (
+ "Draw is within limits. Sensor delta:%f mAmp Baseline:%f "
+ "mAmp Sensor: %f mAmp Stddev : %f mAmp Peak: %f mAmp") % (
+ current_diff * 1000.0, baseline_amps * 1000.0, avg * 1000.0,
+ stddev * 1000.0, max_power * 1000.0)
else:
- message = ("Draw is too high. Current:%f Background:%f Measured: %f Stddev: %f Peak: %f")%\
- ( current_diff*1000.0, backgnd*1000.0, avg*1000.0, stddev*1000.0, max_power*1000.0)
- self.setTestResult( testname = self._current_test,
- condition = current_diff <= max_power_allowed,
- msg = message)
- print("Result: "+message)
+ message = (
+ "Draw is too high. Current:%f Background:%f Measured: %f "
+ "Stddev: %f Peak: %f") % (
+ current_diff * 1000.0, baseline_amps * 1000.0, avg * 1000.0,
+ stddev * 1000.0, max_power * 1000.0)
+ self.setTestResult(
+ self._current_test,
+ ("PASS" if (current_diff <= max_amperes_allowed) else "FAIL"),
+ message)
+ print("Result: " + message)
except:
- import traceback
traceback.print_exc()
- self.setTestResult(self._current_test, condition="FAIL",
- msg="Exception occurred during run of test.")
-
+ self.setTestResult(self._current_test, test_result = "FAIL",
+ test_message = "Exception occurred during run of test.")
+ raise
@staticmethod
- def run_tests():
+ def runTests(max_baseline_amps):
testrunner = None
try:
- GENERIC_MOTION_REQUEST = "\n===> Please press Next and when the screen is off, keep " + \
- "the device under motion with only tiny, slow movements until the screen turns " + \
- "on again.\nPlease refrain from interacting with the screen or pressing any side " + \
- "buttons while measurements are taken."
- USER_STEPS_REQUEST = "\n===> Please press Next and when the screen is off, then move " + \
- "the device to simulate step motion until the screen turns on again.\nPlease " + \
- "refrain from interacting with the screen or pressing any side buttons while " + \
- "measurements are taken."
- testrunner = PowerTest()
- testrunner.executeOnDevice(PowerTest.REQUEST_SHOW_MESSAGE % "Connected. Running tests...")
- testrunner.runPowerTest("SIGNIFICANT_MOTION", PowerTest.MAX_SIGMO_POWER, user_request = GENERIC_MOTION_REQUEST)
- testrunner.runPowerTest("STEP_DETECTOR", PowerTest.MAX_STEP_DETECTOR_POWER, user_request = USER_STEPS_REQUEST)
- testrunner.runPowerTest("STEP_COUNTER", PowerTest.MAX_STEP_COUNTER_POWER, user_request = USER_STEPS_REQUEST)
- testrunner.runPowerTest("ACCELEROMETER", PowerTest.MAX_ACCEL_POWER, user_request = GENERIC_MOTION_REQUEST)
- testrunner.runPowerTest("MAGNETIC_FIELD", PowerTest.MAX_MAG_POWER, user_request = GENERIC_MOTION_REQUEST)
- testrunner.runPowerTest("GYROSCOPE", PowerTest.MAX_GYRO_POWER, user_request = GENERIC_MOTION_REQUEST)
- testrunner.runPowerTest("ACCELEROMETER", PowerTest.MAX_ACCEL_POWER, user_request = None)
- testrunner.runPowerTest("MAGNETIC_FIELD", PowerTest.MAX_MAG_POWER, user_request = None)
- testrunner.runPowerTest("GYROSCOPE", PowerTest.MAX_GYRO_POWER, user_request = None)
- testrunner.runPowerTest("SIGNIFICANT_MOTION", PowerTest.MAX_SIGMO_POWER, user_request = None)
- testrunner.runPowerTest("STEP_DETECTOR", PowerTest.MAX_STEP_DETECTOR_POWER, user_request = None)
- testrunner.runPowerTest("STEP_COUNTER", PowerTest.MAX_STEP_COUNTER_POWER, user_request = None)
+ GENERIC_MOTION_REQUEST = ("\n===> Please press Next and when the "
+ "screen is off, keep the device under motion with only tiny, "
+ "slow movements until the screen turns on again.\nPlease "
+ "refrain from interacting with the screen or pressing any side "
+ "buttons while measurements are taken.")
+ USER_STEPS_REQUEST = ("\n===> Please press Next and when the "
+ "screen is off, then move the device to simulate step motion "
+ "until the screen turns on again.\nPlease refrain from "
+ "interacting with the screen or pressing any side buttons "
+ "while measurements are taken.")
+ testrunner = PowerTest(max_baseline_amps)
+ testrunner.executeOnDevice(
+ PowerTest.REQUEST_SHOW_MESSAGE % "Connected. Running tests...")
+ is_baseline_success, baseline_amps = testrunner.getBaselineState()
+
+ if is_baseline_success:
+ testrunner.setUsbEnabled(True)
+ # TODO: Enable testing a single sensor
+ testrunner.runSensorPowerTest(
+ "SIGNIFICANT_MOTION", PowerTest.MAX_SIGMO_AMPS, baseline_amps,
+ user_request = GENERIC_MOTION_REQUEST)
+ testrunner.runSensorPowerTest(
+ "STEP_DETECTOR", PowerTest.MAX_STEP_DETECTOR_AMPS, baseline_amps,
+ user_request = USER_STEPS_REQUEST)
+ testrunner.runSensorPowerTest(
+ "STEP_COUNTER", PowerTest.MAX_STEP_COUNTER_AMPS, baseline_amps,
+ user_request = USER_STEPS_REQUEST)
+ testrunner.runSensorPowerTest(
+ "ACCELEROMETER", PowerTest.MAX_ACCEL_AMPS, baseline_amps,
+ user_request = GENERIC_MOTION_REQUEST)
+ testrunner.runSensorPowerTest(
+ "MAGNETIC_FIELD", PowerTest.MAX_MAG_AMPS, baseline_amps,
+ user_request = GENERIC_MOTION_REQUEST)
+ testrunner.runSensorPowerTest(
+ "GYROSCOPE", PowerTest.MAX_GYRO_AMPS, baseline_amps,
+ user_request = GENERIC_MOTION_REQUEST)
+ testrunner.runSensorPowerTest(
+ "ACCELEROMETER", PowerTest.MAX_ACCEL_AMPS, baseline_amps,
+ user_request = None)
+ testrunner.runSensorPowerTest(
+ "MAGNETIC_FIELD", PowerTest.MAX_MAG_AMPS, baseline_amps,
+ user_request = None)
+ testrunner.runSensorPowerTest(
+ "GYROSCOPE", PowerTest.MAX_GYRO_AMPS, baseline_amps,
+ user_request = None)
+ testrunner.runSensorPowerTest(
+ "SIGNIFICANT_MOTION", PowerTest.MAX_SIGMO_AMPS, baseline_amps,
+ user_request = None)
+ testrunner.runSensorPowerTest(
+ "STEP_DETECTOR", PowerTest.MAX_STEP_DETECTOR_AMPS, baseline_amps,
+ user_request = None)
+ testrunner.runSensorPowerTest(
+ "STEP_COUNTER", PowerTest.MAX_STEP_COUNTER_AMPS, baseline_amps,
+ user_request = None)
+ else:
+ print("Could not get to baseline state. This is either because "
+ "in several trials, the monitor could not measure a set "
+ "of power measurements that had the specified low "
+ "variance or the mean measurements were below the "
+ "expected value. None of the sensor power measurement "
+ " tests were performed due to not being able to detect "
+ "baseline state. Please re-run the power tests.")
+ except KeyboardInterrupt:
+ print "Keyboard interrupt from user."
+ raise
except:
import traceback
traceback.print_exc()
finally:
- signal_exit_q.put(0) # anything will signal thread to terminate
logging.info("TESTS COMPLETE")
if testrunner:
try:
testrunner.finalize()
except socket.error:
- sys.exit("============================\nUnable to connect to device under " + \
- "test. Make sure the device is connected via the usb pass-through, " + \
- "the CtsVerifier app is running the SensorPowerTest on the device, " + \
- "and USB pass-through is enabled.\n===========================")
-
+ sys.exit(
+ "===================================================\n"
+ "Unable to connect to device under test. Make sure \n"
+ "the device is connected via the usb pass-through, \n"
+ "the CtsVerifier app is running the SensorPowerTest on \n"
+ "the device, and USB pass-through is enabled.\n"
+ "===================================================")
def main(argv):
- """ Simple command-line interface for a power test application."""
- useful_flags = ["voltage", "status", "usbpassthrough",
- "samples", "current", "log", "power_monitor"]
- if not [f for f in useful_flags if FLAGS.get(f, None) is not None]:
- print __doc__.strip()
- print FLAGS.MainModuleHelp()
- return
+ """ Simple command-line interface for a power test application."""
+ useful_flags = ["voltage", "status", "usbpassthrough",
+ "samples", "current", "log", "power_monitor"]
+ if not [f for f in useful_flags if FLAGS.get(f, None) is not None]:
+ print __doc__.strip()
+ print FLAGS.MainModuleHelp()
+ return
- if FLAGS.avg and FLAGS.avg < 0:
- loggign.error("--avg must be greater than 0")
- return
+ if FLAGS.avg and FLAGS.avg < 0:
+ logging.error("--avg must be greater than 0")
+ return
- if FLAGS.voltage is not None:
- if FLAGS.voltage > 5.5:
- print("!!WARNING: Voltage higher than typical values!!!")
- try:
- response = raw_input("Voltage of %.3f requested. Confirm this is correct (Y/N)"%FLAGS.voltage)
- if response.upper() != "Y":
- sys.exit("Aborting")
- except:
- sys.exit("Aborting.")
+ if FLAGS.voltage is not None:
+ if FLAGS.voltage > 5.5:
+ print("!!WARNING: Voltage higher than typical values!!!")
+ try:
+ response = raw_input(
+ "Voltage of %.3f requested. Confirm this is correct (Y/N)" %
+ FLAGS.voltage)
+ if response.upper() != "Y":
+ sys.exit("Aborting")
+ except:
+ sys.exit("Aborting.")
- if not FLAGS.power_monitor:
- sys.exit("You must specify a '--power_monitor' option to specify which power monitor type " + \
- "you are using.\nOne of:\n \n ".join(available_monitors))
- power_monitors = do_import('power_monitors.%s' % FLAGS.power_monitor)
- try:
- mon = power_monitors.Power_Monitor(device=FLAGS.device)
- except:
- import traceback
- traceback.print_exc()
- sys.exit("No power monitors found")
-
- if FLAGS.voltage is not None:
-
- if FLAGS.ramp is not None:
- mon.RampVoltage(mon.start_voltage, FLAGS.voltage)
- else:
- mon.SetVoltage(FLAGS.voltage)
-
- if FLAGS.current is not None:
- mon.SetMaxCurrent(FLAGS.current)
-
- if FLAGS.status:
- items = sorted(mon.GetStatus().items())
- print "\n".join(["%s: %s" % item for item in items])
-
- if FLAGS.usbpassthrough:
- if FLAGS.usbpassthrough == 'off':
- mon.SetUsbPassthrough(0)
- elif FLAGS.usbpassthrough == 'on':
- mon.SetUsbPassthrough(1)
- elif FLAGS.usbpassthrough == 'auto':
- mon.SetUsbPassthrough(2)
- else:
- mon.Close()
- sys.exit('bad pass-through flag: %s' % FLAGS.usbpassthrough)
-
- if FLAGS.samples:
- # Make sure state is normal
- mon.StopDataCollection()
- status = mon.GetStatus()
- native_hz = status["sampleRate"] * 1000
-
- # Collect and average samples as specified
- mon.StartDataCollection()
-
- # In case FLAGS.hz doesn't divide native_hz exactly, use this invariant:
- # 'offset' = (consumed samples) * FLAGS.hz - (emitted samples) * native_hz
- # This is the error accumulator in a variation of Bresenham's algorithm.
- emitted = offset = 0
- collected = []
- history_deque = collections.deque() # past n samples for rolling average
-
- try:
- last_flush = time.time()
- while emitted < FLAGS.samples or FLAGS.samples == -1:
- # The number of raw samples to consume before emitting the next output
- need = (native_hz - offset + FLAGS.hz - 1) / FLAGS.hz
- if need > len(collected): # still need more input samples
- samples = mon.CollectData()
- if not samples: break
- collected.extend(samples)
- else:
- # Have enough data, generate output samples.
- # Adjust for consuming 'need' input samples.
- offset += need * FLAGS.hz
- while offset >= native_hz: # maybe multiple, if FLAGS.hz > native_hz
- this_sample = sum(collected[:need]) / need
-
- if FLAGS.timestamp: print int(time.time()),
-
- if FLAGS.avg:
- history_deque.appendleft(this_sample)
- if len(history_deque) > FLAGS.avg: history_deque.pop()
- print "%f %f" % (this_sample,
- sum(history_deque) / len(history_deque))
- else:
- print "%f" % this_sample
- sys.stdout.flush()
-
- offset -= native_hz
- emitted += 1 # adjust for emitting 1 output sample
- collected = collected[need:]
- now = time.time()
- if now - last_flush >= 0.99: # flush every second
- sys.stdout.flush()
- last_flush = now
- except KeyboardInterrupt:
- print("interrupted")
- return 1
- finally:
- mon.Close()
- return 0
-
- if FLAGS.run:
if not FLAGS.power_monitor:
- sys.exit("When running power tests, you must specify which type of power monitor to use" +
- " with '--power_monitor <type of power monitor>'")
- PowerTest.run_tests()
+ sys.exit(
+ "You must specify a '--power_monitor' option to specify which power "
+ "monitor type " +
+ "you are using.\nOne of:\n \n ".join(available_monitors))
+ power_monitors = do_import("power_monitors.%s" % FLAGS.power_monitor)
+ try:
+ mon = power_monitors.Power_Monitor(device = FLAGS.device)
+ except:
+ import traceback
+ traceback.print_exc()
+ sys.exit("No power monitors found")
+
+ if FLAGS.voltage is not None:
+
+ if FLAGS.ramp is not None:
+ mon.RampVoltage(mon.start_voltage, FLAGS.voltage)
+ else:
+ mon.SetVoltage(FLAGS.voltage)
+
+ if FLAGS.current is not None:
+ mon.SetMaxCurrent(FLAGS.current)
+
+ if FLAGS.status:
+ items = sorted(mon.GetStatus().items())
+ print "\n".join(["%s: %s" % item for item in items])
+
+ if FLAGS.usbpassthrough:
+ if FLAGS.usbpassthrough == "off":
+ mon.SetUsbPassthrough(0)
+ elif FLAGS.usbpassthrough == "on":
+ mon.SetUsbPassthrough(1)
+ elif FLAGS.usbpassthrough == "auto":
+ mon.SetUsbPassthrough(2)
+ else:
+ mon.Close()
+ sys.exit("bad pass-through flag: %s" % FLAGS.usbpassthrough)
+
+ if FLAGS.samples:
+ # Make sure state is normal
+ mon.StopDataCollection()
+ status = mon.GetStatus()
+ native_hz = status["sampleRate"] * 1000
+
+ # Collect and average samples as specified
+ mon.StartDataCollection()
+
+ # In case FLAGS.hz doesn't divide native_hz exactly, use this invariant:
+ # 'offset' = (consumed samples) * FLAGS.hz - (emitted samples) * native_hz
+ # This is the error accumulator in a variation of Bresenham's algorithm.
+ emitted = offset = 0
+ collected = []
+ history_deque = collections.deque() # past n samples for rolling average
+
+ # TODO: Complicated lines of code below. Refactoring needed
+ try:
+ last_flush = time.time()
+ while emitted < FLAGS.samples or FLAGS.samples == -1:
+ # The number of raw samples to consume before emitting the next output
+ need = (native_hz - offset + FLAGS.hz - 1) / FLAGS.hz
+ if need > len(collected): # still need more input samples
+ samples = mon.CollectData()
+ if not samples: break
+ collected.extend(samples)
+ else:
+ # Have enough data, generate output samples.
+ # Adjust for consuming 'need' input samples.
+ offset += need * FLAGS.hz
+ while offset >= native_hz: # maybe multiple, if FLAGS.hz > native_hz
+ this_sample = sum(collected[:need]) / need
+
+ if FLAGS.timestamp: print int(time.time()),
+
+ if FLAGS.avg:
+ history_deque.appendleft(this_sample)
+ if len(history_deque) > FLAGS.avg: history_deque.pop()
+ print "%f %f" % (this_sample,
+ sum(history_deque) / len(history_deque))
+ else:
+ print "%f" % this_sample
+ sys.stdout.flush()
+
+ offset -= native_hz
+ emitted += 1 # adjust for emitting 1 output sample
+ collected = collected[need:]
+ now = time.time()
+ if now - last_flush >= 0.99: # flush every second
+ sys.stdout.flush()
+ last_flush = now
+ except KeyboardInterrupt:
+ print("interrupted")
+ return 1
+ finally:
+ mon.Close()
+ return 0
+
+ if FLAGS.run:
+ if not FLAGS.power_monitor:
+ sys.exit(
+ "When running power tests, you must specify which type of power "
+ "monitor to use" +
+ " with '--power_monitor <type of power monitor>'")
+ try:
+ PowerTest.runTests(FLAGS.max_baseline_amps)
+ except KeyboardInterrupt:
+ print "Keyboard interrupt from user"
if __name__ == "__main__":
flags.DEFINE_boolean("status", None, "Print power meter status")
@@ -581,5 +932,6 @@
flags.DEFINE_boolean("log", False, "Log progress to a file or not")
flags.DEFINE_boolean("run", False, "Run the test suite for power")
flags.DEFINE_string("power_monitor", None, "Type of power monitor to use")
+ flags.DEFINE_float("max_baseline_amps", 0.005,
+ "Set maximum baseline current for device being tested")
sys.exit(main(FLAGS(sys.argv)))
-
diff --git a/apps/CtsVerifier/res/drawable-hdpi/ic_corp_icon.png b/apps/CtsVerifier/res/drawable-hdpi/ic_corp_icon.png
new file mode 100644
index 0000000..06c5135
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable-hdpi/ic_corp_icon.png
Binary files differ
diff --git a/apps/CtsVerifier/res/drawable-mdpi/ic_corp_icon.png b/apps/CtsVerifier/res/drawable-mdpi/ic_corp_icon.png
new file mode 100644
index 0000000..79372b2
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable-mdpi/ic_corp_icon.png
Binary files differ
diff --git a/apps/CtsVerifier/res/drawable-xhdpi/ic_corp_icon.png b/apps/CtsVerifier/res/drawable-xhdpi/ic_corp_icon.png
new file mode 100644
index 0000000..3626c7d
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable-xhdpi/ic_corp_icon.png
Binary files differ
diff --git a/apps/CtsVerifier/res/drawable-xxhdpi/ic_corp_icon.png b/apps/CtsVerifier/res/drawable-xxhdpi/ic_corp_icon.png
new file mode 100644
index 0000000..d33319f
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable-xxhdpi/ic_corp_icon.png
Binary files differ
diff --git a/apps/CtsVerifier/res/drawable-xxxhdpi/ic_corp_icon.png b/apps/CtsVerifier/res/drawable-xxxhdpi/ic_corp_icon.png
new file mode 100644
index 0000000..359e210
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable-xxxhdpi/ic_corp_icon.png
Binary files differ
diff --git a/apps/CtsVerifier/res/layout/byod_custom_view_badged_icons.xml b/apps/CtsVerifier/res/layout/byod_custom_view.xml
similarity index 89%
rename from apps/CtsVerifier/res/layout/byod_custom_view_badged_icons.xml
rename to apps/CtsVerifier/res/layout/byod_custom_view.xml
index 1a29f65..00c9ad9 100644
--- a/apps/CtsVerifier/res/layout/byod_custom_view_badged_icons.xml
+++ b/apps/CtsVerifier/res/layout/byod_custom_view.xml
@@ -28,12 +28,10 @@
<TextView android:id="@+id/message"
style="@style/InstructionsSmallFont"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/provisioning_byod_workapps_visible_instruction" />
+ android:layout_height="wrap_content" />
</ScrollView>
<ImageView android:id="@+id/sample_icon"
- android:src="@drawable/badged_icon"
android:layout_width="56dip"
android:layout_height="56dip"
android:layout_gravity="center_horizontal" />
diff --git a/apps/CtsVerifier/res/layout/fs_info.xml b/apps/CtsVerifier/res/layout/fs_info.xml
index 3fe6815..ea02fee 100644
--- a/apps/CtsVerifier/res/layout/fs_info.xml
+++ b/apps/CtsVerifier/res/layout/fs_info.xml
@@ -13,62 +13,44 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent" android:layout_height="wrap_content">
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent" android:layout_height="fill_parent">
+ <GridLayout
+ android:columnCount="2"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
- <ImageView android:id="@+id/fs_legend_good_image"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/fs_good"
- android:layout_alignParentTop="true"
- android:layout_alignParentLeft="true" />
- <TextView android:id="@+id/fs_legend_good_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/fs_legend_good"
- android:layout_toRightOf="@id/fs_legend_good_image"
- android:layout_alignTop="@id/fs_legend_good_image"
- android:layout_marginLeft="3dip" />
+ <ImageView android:id="@+id/fs_legend_good_image"
+ android:src="@drawable/fs_good"
+ android:layout_gravity="top|left" />
+ <TextView android:id="@+id/fs_legend_good_text"
+ android:text="@string/fs_legend_good"
+ android:layout_marginLeft="3dip"
+ android:layout_gravity="top|left" />
- <ImageView android:id="@+id/fs_legend_indeterminate_image"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/fs_indeterminate"
- android:layout_alignParentLeft="true"
- android:layout_below="@id/fs_legend_good_image" />
- <TextView android:id="@+id/fs_legend_indeterminate_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/fs_legend_indeterminate"
- android:layout_toRightOf="@id/fs_legend_indeterminate_image"
- android:layout_alignTop="@id/fs_legend_indeterminate_image"
- android:layout_marginLeft="3dip" />
+ <ImageView android:id="@+id/fs_legend_indeterminate_image"
+ android:src="@drawable/fs_indeterminate"
+ android:layout_gravity="top|left" />
+ <TextView android:id="@+id/fs_legend_indeterminate_text"
+ android:text="@string/fs_legend_indeterminate"
+ android:layout_marginLeft="3dip"
+ android:layout_gravity="top|left" />
- <ImageView android:id="@+id/fs_legend_warning_image"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/fs_warning"
- android:layout_alignParentLeft="true"
- android:layout_below="@id/fs_legend_indeterminate_image" />
- <TextView android:id="@+id/fs_legend_warning_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/fs_legend_warning"
- android:layout_toRightOf="@id/fs_legend_warning_image"
- android:layout_alignTop="@id/fs_legend_warning_image"
- android:layout_marginLeft="3dip" />
+ <ImageView android:id="@+id/fs_legend_warning_image"
+ android:src="@drawable/fs_warning"
+ android:layout_gravity="top|left" />
+ <TextView android:id="@+id/fs_legend_warning_text"
+ android:text="@string/fs_legend_warning"
+ android:layout_marginLeft="3dip"
+ android:layout_gravity="top|left" />
- <ImageView android:id="@+id/fs_legend_error_image"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/fs_error"
- android:layout_alignParentLeft="true"
- android:layout_below="@id/fs_legend_warning_image" />
- <TextView android:id="@+id/fs_legend_error_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/fs_legend_error"
- android:layout_toRightOf="@id/fs_legend_error_image"
- android:layout_alignTop="@id/fs_legend_error_image"
- android:layout_marginLeft="3dip" />
-</RelativeLayout>
+ <ImageView android:id="@+id/fs_legend_error_image"
+ android:src="@drawable/fs_error"
+ android:layout_gravity="top|left" />
+ <TextView android:id="@+id/fs_legend_error_text"
+ android:text="@string/fs_legend_error"
+ android:layout_marginLeft="3dip"
+ android:layout_gravity="top|left" />
+ </GridLayout>
+</ScrollView>
diff --git a/apps/CtsVerifier/res/layout/screen_pinning.xml b/apps/CtsVerifier/res/layout/screen_pinning.xml
new file mode 100644
index 0000000..f1d7b65
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/screen_pinning.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <TextView
+ android:id="@+id/error_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true" >
+
+ <RelativeLayout
+ android:id="@+id/instructions_group"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+
+ <LinearLayout
+ android:id="@+id/instructions_list"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+
+ </LinearLayout>
+
+ <Button
+ android:id="@+id/next_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:layout_below="@id/instructions_list"
+ android:text="@string/next_button_text" />
+
+ </RelativeLayout>
+ </ScrollView>
+
+ <include
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ layout="@layout/pass_fail_buttons" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index af269c6..b92bca9 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -443,6 +443,10 @@
<string name="nfc_hce_payment_dynamic_aids_reader">Dynamic payment AIDs (Reader)</string>
<string name="nfc_hce_payment_dynamic_aids_help">This test tries to register dynamic AIDs for a payment service.</string>
+ <string name="nfc_hce_large_num_aids_emulator">Large number of AIDs (Emulator)</string>
+ <string name="nfc_hce_large_num_aids_reader">Large number of AIDs (Reader)</string>
+ <string name="nfc_hce_large_num_aids_help">This test tries to register a large number of different AIDs, to make sure there are no limitations on the maximum amount of HCE apps on the device. Note that this test may take a few seconds to complete; please be patient.</string>
+
<string name="nfc_hce_payment_prefix_aids_emulator">Payment prefix AIDs (Emulator)</string>
<string name="nfc_hce_payment_prefix_aids_reader">Payment prefix AIDs (Reader)</string>
<string name="nfc_hce_payment_prefix_aids_help">This test statically registers prefix AIDs for a payment service.</string>
@@ -1100,6 +1104,20 @@
<string name="widget_pass">Pass</string>
<string name="widget_fail">Fail</string>
+ <string name="provisioning_byod_nonmarket_allow">Enable non-market apps</string>
+ <string name="provisioning_byod_nonmarket_allow_info">
+ This test verifies that non-market apps can be installed if permitted.\n
+ 1. A package installation UI should appear.\n
+ 2. Accept the package and verify that it installs.
+ </string>
+
+ <string name="provisioning_byod_nonmarket_deny">Disable non-market apps</string>
+ <string name="provisioning_byod_nonmarket_deny_info">
+ This test verifies that non-market apps cannot be installed unless permitted.\n
+ 1. A package installation UI should appear.\n
+ 2. Verify that the installation of the package is refused.
+ </string>
+
<!-- Strings for DeskClock -->
<string name="deskclock_tests">Alarms and Timers Tests</string>
<string name="deskclock_tests_info">
@@ -1269,6 +1287,13 @@
\n
Verify that you are prompted with the above choices and both options work as intended. Then mark this test accordingly.
</string>
+ <string name="provisioning_byod_work_notification">Work notification is badged</string>
+ <string name="provisioning_byod_work_notification_instruction">
+ Please press the Go button to trigger a notification.\n
+ \n
+ Verify that the notification is badged (see sample badge below). Then mark this test accordingly.
+ </string>
+ <string name="provisioning_byod_work_notification_title">This is a work notification</string>
<string name="provisioning_byod_profile_visible_instruction">
Please press the Go button to open the Settings page.
Navigate to Accounts and confirm that:\n
@@ -1422,4 +1447,18 @@
<!-- A list of fully-qualified test classes that should not be run. -->
<string-array name="disabled_tests" />
+
+ <!-- Strings for screen pinning test -->
+ <string name="screen_pinning_test">Screen Pinning Test</string>
+ <string name="screen_pin_instructions">Pressing next will prompt you to enter screen pinning, allow this app to enter screen pinning.</string>
+ <string name="screen_pin_check_pinned">Press Next to verify the app is pinned.</string>
+ <string name="screen_pin_no_exit">Try to leave the app without unpinning the screen. Press next once you have verified you cannot leave.</string>
+ <string name="screen_pin_exit">Use interactions defined by your device to unpin such as long pressing the back and overview button, then press next.</string>
+ <string name="screen_pinning_done">All tests completed successfully.</string>
+
+ <string name="error_screen_no_longer_pinned">The screen was no longer pinned.</string>
+ <string name="error_screen_already_pinned">Cannot start the test with the screen already pinned.</string>
+ <string name="error_screen_pinning_did_not_start">Screen was not pinned.</string>
+ <string name="error_screen_pinning_did_not_exit">Screen was not unpinned.</string>
+ <string name="error_screen_pinning_couldnt_exit">Could not exit screen pinning through API.</string>
</resources>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/ManifestTestListAdapter.java b/apps/CtsVerifier/src/com/android/cts/verifier/ManifestTestListAdapter.java
index ebddf4f..6b9316f 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/ManifestTestListAdapter.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/ManifestTestListAdapter.java
@@ -105,18 +105,20 @@
private String mTestParent;
- public ManifestTestListAdapter(Context context, String testParent) {
+ public ManifestTestListAdapter(Context context, String testParent, String[] disabledTestArray) {
super(context);
mContext = context;
mTestParent = testParent;
-
- String[] disabledTestArray = context.getResources().getStringArray(R.array.disabled_tests);
mDisabledTests = new HashSet<>(disabledTestArray.length);
for (int i = 0; i < disabledTestArray.length; i++) {
mDisabledTests.add(disabledTestArray[i]);
}
}
+ public ManifestTestListAdapter(Context context, String testParent) {
+ this(context, testParent, context.getResources().getStringArray(R.array.disabled_tests));
+ }
+
@Override
protected List<TestListItem> getRows() {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerPowerLevelActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerPowerLevelActivity.java
index a6489c1..bf3484e 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerPowerLevelActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerPowerLevelActivity.java
@@ -59,6 +59,7 @@
setPassFailButtonClickListeners();
setInfoResources(R.string.ble_power_level_name,
R.string.ble_power_level_info, -1);
+ getPassButton().setEnabled(false);
mTimerText = (TextView)findViewById(R.id.ble_timer);
mTimer = new CountDownTimer(REFRESH_MAC_TIME, 1000) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BluetoothTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BluetoothTestActivity.java
index 9895f02..56d73aa 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BluetoothTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BluetoothTestActivity.java
@@ -20,7 +20,12 @@
import com.android.cts.verifier.PassFailButtons;
import com.android.cts.verifier.R;
+import android.bluetooth.BluetoothAdapter;
import android.os.Bundle;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+import java.util.List;
public class BluetoothTestActivity extends PassFailButtons.TestListActivity {
@@ -31,6 +36,32 @@
setPassFailButtonClickListeners();
setInfoResources(R.string.bluetooth_test, R.string.bluetooth_test_info, -1);
- setTestListAdapter(new ManifestTestListAdapter(this, getClass().getName()));
+ BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+ if (bluetoothAdapter == null) {
+ Toast.makeText(this, "bluetooth not supported", Toast.LENGTH_SHORT);
+ return;
+ }
+
+ List<String> disabledTestArray = new ArrayList<String>();
+ for (String s : this.getResources().getStringArray(R.array.disabled_tests)) {
+ disabledTestArray.add(s);
+ }
+ if (!this.getPackageManager().hasSystemFeature("android.hardware.bluetooth_le")) {
+ disabledTestArray.add(
+ "com.android.cts.verifier.bluetooth.BleAdvertiserTestActivity");
+ disabledTestArray.add(
+ "com.android.cts.verifier.bluetooth.BleScannerTestActivity");
+ disabledTestArray.add(
+ "com.android.cts.verifier.bluetooth.BleClientTestActivity");
+ disabledTestArray.add(
+ "com.android.cts.verifier.bluetooth.BleServerStartActivity");
+ } else if (!bluetoothAdapter.isMultipleAdvertisementSupported()) {
+ disabledTestArray.add(
+ "com.android.cts.verifier.bluetooth.BleAdvertiserTestActivity");
+ disabledTestArray.add(
+ "com.android.cts.verifier.bluetooth.BleServerStartActivity");
+ }
+ setTestListAdapter(new ManifestTestListAdapter(this, getClass().getName(),
+ disabledTestArray.toArray(new String[disabledTestArray.size()])));
}
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
index b697be8..057d00d 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
@@ -75,6 +75,9 @@
private TestItem mDeviceAdminVisibleTest;
private TestItem mWorkAppVisibleTest;
private TestItem mCrossProfileIntentFiltersTest;
+ private TestItem mDisableNonMarketTest;
+ private TestItem mEnableNonMarketTest;
+ private TestItem mWorkNotificationBadgedTest;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -168,16 +171,27 @@
* To keep the image in this test up to date, use the instructions in
* {@link ByodIconSamplerActivity}.
*/
- mWorkAppVisibleTest = new TestItem(this, R.string.provisioning_byod_workapps_visible,
+ mWorkAppVisibleTest = new TestItemWithIcon(this,
+ R.string.provisioning_byod_workapps_visible,
R.string.provisioning_byod_profile_visible_instruction,
- new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)) {
- @Override
- public View getCustomView() {
- LayoutInflater layoutInflater = LayoutInflater.from(getApplicationContext());
- return layoutInflater.inflate(R.layout.byod_custom_view_badged_icons,
- null /* root */);
- }
- };
+ new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME),
+ R.drawable.badged_icon);
+
+ mWorkNotificationBadgedTest = new TestItemWithIcon(this,
+ R.string.provisioning_byod_work_notification,
+ R.string.provisioning_byod_work_notification_instruction,
+ new Intent(WorkNotificationTestActivity.ACTION_WORK_NOTIFICATION),
+ R.drawable.ic_corp_icon);
+
+ mDisableNonMarketTest = new TestItem(this, R.string.provisioning_byod_nonmarket_deny,
+ R.string.provisioning_byod_nonmarket_deny_info,
+ new Intent(ByodHelperActivity.ACTION_INSTALL_APK)
+ .putExtra(ByodHelperActivity.EXTRA_ALLOW_NON_MARKET_APPS, false));
+
+ mEnableNonMarketTest = new TestItem(this, R.string.provisioning_byod_nonmarket_allow,
+ R.string.provisioning_byod_nonmarket_allow_info,
+ new Intent(ByodHelperActivity.ACTION_INSTALL_APK)
+ .putExtra(ByodHelperActivity.EXTRA_ALLOW_NON_MARKET_APPS, true));
Intent intent = new Intent(CrossProfileTestActivity.ACTION_CROSS_PROFILE);
Intent chooser = Intent.createChooser(intent, getResources().getString(R.string.provisioning_cross_profile_chooser));
@@ -190,7 +204,10 @@
mTests.add(mProfileVisibleTest);
mTests.add(mDeviceAdminVisibleTest);
mTests.add(mWorkAppVisibleTest);
+ mTests.add(mWorkNotificationBadgedTest);
mTests.add(mCrossProfileIntentFiltersTest);
+ mTests.add(mDisableNonMarketTest);
+ mTests.add(mEnableNonMarketTest);
}
@Override
@@ -208,12 +225,14 @@
.setPositiveButton(R.string.pass_button_text, new AlertDialog.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
+ clearRemainingState(test);
setTestResult(test, TestResult.Passed);
}
})
.setNegativeButton(R.string.fail_button_text, new AlertDialog.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
+ clearRemainingState(test);
setTestResult(test, TestResult.Failed);
}
});
@@ -234,6 +253,14 @@
});
}
+ private void clearRemainingState(final TestItem test) {
+ if (WorkNotificationTestActivity.ACTION_WORK_NOTIFICATION.equals(
+ test.getManualTestIntent().getAction())) {
+ ByodFlowTestActivity.this.startActivity(new Intent(
+ WorkNotificationTestActivity.ACTION_CLEAR_WORK_NOTIFICATION));
+ }
+ }
+
private void setTestResult(TestItem test, TestResult result) {
test.setPassFailState(result);
@@ -292,6 +319,10 @@
this, ByodHelperActivity.class),
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
+ getPackageManager().setComponentEnabledSetting(new ComponentName(
+ this, WorkNotificationTestActivity.class),
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+ PackageManager.DONT_KILL_APP);
}
private void showToast(int messageId) {
@@ -361,6 +392,29 @@
}
}
+ static class TestItemWithIcon extends TestItem {
+
+ private int mImageResId;
+ private Context mContext;
+
+ public TestItemWithIcon(Context context, int nameResId, int testInstructionResId,
+ Intent testIntent, int imageResId) {
+ super(context, nameResId, testInstructionResId, testIntent);
+ mContext = context;
+ mImageResId = imageResId;
+ }
+
+ @Override
+ public View getCustomView() {
+ LayoutInflater layoutInflater = LayoutInflater.from(mContext);
+ View view = layoutInflater.inflate(R.layout.byod_custom_view,
+ null /* root */);
+ ((ImageView) view.findViewById(R.id.sample_icon)).setImageResource(mImageResId);
+ ((TextView) view.findViewById(R.id.message)).setText(getManualTestInstruction());
+ return view;
+ }
+ }
+
static class TestAdapter extends ArrayAdapter<TestItem> {
public TestAdapter(Context context) {
@@ -389,5 +443,4 @@
return view;
}
}
-
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
index 5dac4bd..277324c 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
@@ -24,10 +24,14 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.net.Uri;
import android.os.Bundle;
+import android.provider.Settings;
import android.util.Log;
import android.widget.Toast;
+import static android.provider.Settings.Secure.INSTALL_NON_MARKET_APPS;
+
import com.android.cts.verifier.R;
import com.android.cts.verifier.managedprovisioning.ByodFlowTestActivity.TestResult;
@@ -53,17 +57,33 @@
public static final String EXTRA_PROVISIONED = "extra_provisioned";
- private ComponentName mAdminReceiverComponent;
+ // Primary -> managed intent: set unknown sources restriction and install package
+ public static final String ACTION_INSTALL_APK = "com.android.cts.verifier.managedprovisioning.BYOD_INSTALL_APK";
+ public static final String EXTRA_ALLOW_NON_MARKET_APPS = INSTALL_NON_MARKET_APPS;
+ private static final int REQUEST_INSTALL_PACKAGE = 1;
+
+ private static final String ORIGINAL_SETTINGS_NAME = "original settings";
+ private Bundle mOriginalSettings;
+
+ private ComponentName mAdminReceiverComponent;
private DevicePolicyManager mDevicePolicyManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ if (savedInstanceState != null) {
+ Log.w(TAG, "Restored state");
+ mOriginalSettings = savedInstanceState.getBundle(ORIGINAL_SETTINGS_NAME);
+ } else {
+ mOriginalSettings = new Bundle();
+ }
+
mAdminReceiverComponent = new ComponentName(this, DeviceAdminTestReceiver.class.getName());
mDevicePolicyManager = (DevicePolicyManager) getSystemService(
Context.DEVICE_POLICY_SERVICE);
- String action = getIntent().getAction();
+ Intent intent = getIntent();
+ String action = intent.getAction();
Log.d(TAG, "ByodHelperActivity.onCreate: " + action);
// we are explicitly started by {@link DeviceAdminTestReceiver} after a successful provisioning.
@@ -83,16 +103,70 @@
mDevicePolicyManager.wipeData(0);
showToast(R.string.provisioning_byod_profile_deleted);
}
+ } else if (action.equals(ACTION_INSTALL_APK)) {
+ boolean allowNonMarket = intent.getBooleanExtra(EXTRA_ALLOW_NON_MARKET_APPS, false);
+ boolean wasAllowed = getAllowNonMarket();
+
+ // Update permission to install non-market apps
+ setAllowNonMarket(allowNonMarket);
+ mOriginalSettings.putBoolean(INSTALL_NON_MARKET_APPS, wasAllowed);
+
+ // Request to install a non-market application- easiest way is to reinstall ourself
+ final Intent installIntent = new Intent(Intent.ACTION_INSTALL_PACKAGE)
+ .setData(Uri.parse("package:" + getPackageName()))
+ .putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true)
+ .putExtra(Intent.EXTRA_RETURN_RESULT, true);
+ startActivityForResult(installIntent, REQUEST_INSTALL_PACKAGE);
+
+ // Not yet ready to finish- wait until the result comes back
+ return;
}
// This activity has no UI and is only used to respond to CtsVerifier in the primary side.
finish();
}
+ @Override
+ protected void onSaveInstanceState(final Bundle savedState) {
+ super.onSaveInstanceState(savedState);
+
+ savedState.putBundle(ORIGINAL_SETTINGS_NAME, mOriginalSettings);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case REQUEST_INSTALL_PACKAGE: {
+ Log.w(TAG, "Received REQUEST_INSTALL_PACKAGE, resultCode = " + resultCode);
+ if (mOriginalSettings.containsKey(INSTALL_NON_MARKET_APPS)) {
+ // Restore original setting
+ setAllowNonMarket(mOriginalSettings.getBoolean(INSTALL_NON_MARKET_APPS));
+ mOriginalSettings.remove(INSTALL_NON_MARKET_APPS);
+ }
+ finish();
+ break;
+ }
+ default: {
+ Log.wtf(TAG, "Unknown requestCode " + requestCode + "; data = " + data);
+ break;
+ }
+ }
+ }
+
private boolean isProfileOwner() {
return mDevicePolicyManager.isAdminActive(mAdminReceiverComponent) &&
mDevicePolicyManager.isProfileOwnerApp(mAdminReceiverComponent.getPackageName());
}
+ private boolean getAllowNonMarket() {
+ String value = Settings.Secure.getString(getContentResolver(), INSTALL_NON_MARKET_APPS);
+ return "1".equals(value);
+ }
+
+ private void setAllowNonMarket(boolean allow) {
+ mDevicePolicyManager.setSecureSetting(mAdminReceiverComponent, INSTALL_NON_MARKET_APPS,
+ (allow ? "1" : "0"));
+ }
+
private void startActivityInPrimary(Intent intent) {
// Disable app components in the current profile, so only the counterpart in the other
// profile can respond (via cross-profile intent filter)
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
index 8dccac3..fa7bc4c 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
@@ -26,8 +26,6 @@
import android.util.Log;
import android.widget.Toast;
-import com.android.cts.verifier.managedprovisioning.ByodHelperActivity;
-
/**
* Profile owner receiver for BYOD flow test.
* Setup cross-profile intent filter after successful provisioning.
@@ -50,7 +48,10 @@
IntentFilter filter = new IntentFilter();
filter.addAction(ByodHelperActivity.ACTION_QUERY_PROFILE_OWNER);
filter.addAction(ByodHelperActivity.ACTION_REMOVE_PROFILE_OWNER);
+ filter.addAction(ByodHelperActivity.ACTION_INSTALL_APK);
filter.addAction(CrossProfileTestActivity.ACTION_CROSS_PROFILE);
+ filter.addAction(WorkNotificationTestActivity.ACTION_WORK_NOTIFICATION);
+ filter.addAction(WorkNotificationTestActivity.ACTION_CLEAR_WORK_NOTIFICATION);
dpm.addCrossProfileIntentFilter(getWho(context), filter,
DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/WorkNotificationTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/WorkNotificationTestActivity.java
new file mode 100644
index 0000000..c85ccf5
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/WorkNotificationTestActivity.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2012 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.cts.verifier.managedprovisioning;
+
+import android.app.Activity;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.Bundle;
+
+import com.android.cts.verifier.R;
+
+/**
+ * Test activity used to generate a notification.
+ */
+public class WorkNotificationTestActivity extends Activity {
+ public static final String ACTION_WORK_NOTIFICATION =
+ "com.android.cts.verifier.managedprovisioning.WORK_NOTIFICATION";
+ public static final String ACTION_CLEAR_WORK_NOTIFICATION =
+ "com.android.cts.verifier.managedprovisioning.CLEAR_WORK_NOTIFICATION";
+ private static final int NOTIFICATION_ID = 7;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ final String action = getIntent().getAction();
+ final NotificationManager notificationManager = (NotificationManager)
+ getSystemService(Context.NOTIFICATION_SERVICE);
+ if (ACTION_WORK_NOTIFICATION.equals(action)) {
+ final Notification notification = new Notification.Builder(this)
+ .setSmallIcon(R.drawable.icon)
+ .setContentTitle(getString(R.string.provisioning_byod_work_notification_title))
+ .setVisibility(Notification.VISIBILITY_PUBLIC)
+ .setAutoCancel(true)
+ .build();
+ notificationManager.notify(NOTIFICATION_ID, notification);
+ } else if (ACTION_CLEAR_WORK_NOTIFICATION.equals(action)) {
+ notificationManager.cancel(NOTIFICATION_ID);
+ }
+ finish();
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/BaseEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/BaseEmulatorActivity.java
index 8aa82b5..5f3a10a 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/BaseEmulatorActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/BaseEmulatorActivity.java
@@ -45,7 +45,8 @@
PrefixPaymentService2.COMPONENT,
PrefixTransportService1.COMPONENT,
PrefixTransportService2.COMPONENT,
- PrefixAccessService.COMPONENT)
+ PrefixAccessService.COMPONENT,
+ LargeNumAidsService.COMPONENT)
);
@Override
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceEmulatorTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceEmulatorTestActivity.java
index 87fa3d1..b30bb73 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceEmulatorTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceEmulatorTestActivity.java
@@ -104,6 +104,10 @@
new Intent(this, OnAndOffHostEmulatorActivity.class), null));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ adapter.add(TestListItem.newTest(this, R.string.nfc_hce_large_num_aids_emulator,
+ LargeNumAidsEmulatorActivity.class.getName(),
+ new Intent(this, LargeNumAidsEmulatorActivity.class), null));
+
adapter.add(TestListItem.newTest(this, R.string.nfc_hce_payment_dynamic_aids_emulator,
DynamicAidEmulatorActivity.class.getName(),
new Intent(this, DynamicAidEmulatorActivity.class), null));
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceReaderTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceReaderTestActivity.java
index 4c77871..035ce86 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceReaderTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceReaderTestActivity.java
@@ -103,6 +103,10 @@
SimpleReaderActivity.class.getName(),
DynamicAidEmulatorActivity.buildReaderIntent(this), null));
+ adapter.add(TestListItem.newTest(this, R.string.nfc_hce_large_num_aids_reader,
+ SimpleReaderActivity.class.getName(),
+ LargeNumAidsEmulatorActivity.buildReaderIntent(this), null));
+
NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
CardEmulation cardEmulation = CardEmulation.getInstance(nfcAdapter);
if (cardEmulation.supportsAidPrefixRegistration()) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceUtils.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceUtils.java
index c67169a..698948b 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceUtils.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceUtils.java
@@ -22,6 +22,9 @@
public static final String TRANSPORT_PREFIX_AID = "F001020304";
public static final String ACCESS_PREFIX_AID = "F005060708";
+ public static final String LARGE_NUM_AIDS_PREFIX = "F00102030414";
+ public static final String LARGE_NUM_AIDS_POSTFIX ="81";
+
public static void enableComponent(PackageManager pm, ComponentName component) {
pm.setComponentEnabledSetting(
component,
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/LargeNumAidsEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/LargeNumAidsEmulatorActivity.java
new file mode 100644
index 0000000..db58252
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/LargeNumAidsEmulatorActivity.java
@@ -0,0 +1,59 @@
+package com.android.cts.verifier.nfc.hce;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.nfc.cardemulation.CardEmulation;
+import android.os.Bundle;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.nfc.NfcDialogs;
+
+import java.util.ArrayList;
+
+public class LargeNumAidsEmulatorActivity extends BaseEmulatorActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.pass_fail_text);
+ setPassFailButtonClickListeners();
+ getPassButton().setEnabled(false);
+ setupServices(this, LargeNumAidsService.COMPONENT);
+ }
+
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ }
+
+
+ @Override
+ void onServicesSetup(boolean result) {
+ ArrayList<String> aids = new ArrayList<String>();
+ for (int i = 0; i < 256; i++) {
+ aids.add(HceUtils.LARGE_NUM_AIDS_PREFIX + String.format("%02X", i) + HceUtils.LARGE_NUM_AIDS_POSTFIX);
+ }
+ mCardEmulation.registerAidsForService(LargeNumAidsService.COMPONENT,
+ CardEmulation.CATEGORY_OTHER, aids);
+ }
+
+ @Override
+ void onApduSequenceComplete(ComponentName component, long duration) {
+ if (component.equals(LargeNumAidsService.COMPONENT)) {
+ getPassButton().setEnabled(true);
+ }
+ }
+
+ public static Intent buildReaderIntent(Context context) {
+ Intent readerIntent = new Intent(context, SimpleReaderActivity.class);
+ readerIntent.putExtra(SimpleReaderActivity.EXTRA_APDUS,
+ LargeNumAidsService.getCommandSequence());
+ readerIntent.putExtra(SimpleReaderActivity.EXTRA_RESPONSES,
+ LargeNumAidsService.getResponseSequence());
+ readerIntent.putExtra(SimpleReaderActivity.EXTRA_LABEL,
+ context.getString(R.string.nfc_hce_large_num_aids_reader));
+ return readerIntent;
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/LargeNumAidsService.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/LargeNumAidsService.java
new file mode 100644
index 0000000..5883543
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/LargeNumAidsService.java
@@ -0,0 +1,37 @@
+package com.android.cts.verifier.nfc.hce;
+
+import android.content.ComponentName;
+
+public class LargeNumAidsService extends HceService {
+ static final String TAG = "LargeNumAidsService";
+
+ static final ComponentName COMPONENT =
+ new ComponentName("com.android.cts.verifier",
+ LargeNumAidsService.class.getName());
+
+ public static final CommandApdu[] getCommandSequence() {
+ CommandApdu[] commands = new CommandApdu[256];
+ for (int i = 0; i < 256; i++) {
+ commands[i] = HceUtils.buildSelectApdu(HceUtils.LARGE_NUM_AIDS_PREFIX + String.format("%02X", i) +
+ HceUtils.LARGE_NUM_AIDS_POSTFIX, true);
+ }
+ return commands;
+ }
+
+ public static final String[] getResponseSequence() {
+ String[] responses = new String[256];
+ for (int i = 0; i < 256; i++) {
+ responses[i] = "9000" + String.format("%02X", i);
+ }
+ return responses;
+ }
+
+ public LargeNumAidsService() {
+ initialize(getCommandSequence(), getResponseSequence());
+ }
+
+ @Override
+ public ComponentName getComponent() {
+ return COMPONENT;
+ }
+}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/screenpinning/ScreenPinningTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/screenpinning/ScreenPinningTestActivity.java
new file mode 100644
index 0000000..200865e
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/screenpinning/ScreenPinningTestActivity.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2015 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.cts.verifier.screenpinning;
+
+import android.app.ActivityManager;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+public class ScreenPinningTestActivity extends PassFailButtons.Activity {
+
+ private static final String TAG = "ScreenPinningTestActivity";
+ private static final String KEY_CURRENT_TEST = "keyCurrentTest";
+
+ private Test[] mTests;
+ private int mTestIndex;
+
+ private ActivityManager mActivityManager;
+ private Button mNextButton;
+ private LinearLayout mInstructions;
+
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.screen_pinning);
+ setPassFailButtonClickListeners();
+
+ mTests = new Test[] {
+ // Verify not already pinned.
+ mCheckStartedUnpinned,
+ // Enter pinning, verify pinned, try leaving and have the user exit.
+ mCheckStartPinning,
+ mCheckIsPinned,
+ mCheckTryLeave,
+ mCheckUnpin,
+ // Enter pinning, verify pinned, and use APIs to exit.
+ mCheckStartPinning,
+ mCheckIsPinned,
+ mCheckUnpinFromCode,
+ // All done.
+ mDone,
+ };
+
+ mActivityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
+ mInstructions = (LinearLayout) findViewById(R.id.instructions_list);
+
+ mNextButton = (Button) findViewById(R.id.next_button);
+ mNextButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if ((mTestIndex >= 0) && (mTestIndex < mTests.length)) {
+ mTests[mTestIndex].onNextClick();
+ }
+ }
+ });
+
+ // Don't allow pass until all tests complete.
+ findViewById(R.id.pass_button).setVisibility(View.GONE);
+
+ // Figure out if we are in a test or starting from the beginning.
+ if (savedInstanceState != null && savedInstanceState.containsKey(KEY_CURRENT_TEST)) {
+ mTestIndex = savedInstanceState.getInt(KEY_CURRENT_TEST);
+ } else {
+ mTestIndex = 0;
+ }
+ // Display any pre-existing text.
+ for (int i = 0; i < mTestIndex; i++) {
+ mTests[i].showText();
+ }
+ mTests[mTestIndex].run();
+ };
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ outState.putInt(KEY_CURRENT_TEST, mTestIndex);
+ }
+
+ @Override
+ public void onBackPressed() {
+ // Block back button so we can test screen pinning exit functionality.
+ // Users can still leave by pressing fail (or when done the pass) button.
+ }
+
+ private void show(int id) {
+ TextView tv = new TextView(this);
+ tv.setPadding(10, 10, 10, 30);
+ tv.setText(id);
+ mInstructions.addView(tv);
+ }
+
+ private void succeed() {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mTestIndex++;
+ if (mTestIndex < mTests.length) {
+ mTests[mTestIndex].run();
+ } else {
+ mNextButton.setVisibility(View.GONE);
+ findViewById(R.id.pass_button).setVisibility(View.VISIBLE);
+ }
+ }
+ });
+ }
+
+ private void error(int errorId) {
+ error(errorId, new Throwable());
+ }
+
+ private void error(final int errorId, final Throwable cause) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ String error = getString(errorId);
+ Log.d(TAG, error, cause);
+ // No more instructions needed.
+ findViewById(R.id.instructions_group).setVisibility(View.GONE);
+
+ ((TextView) findViewById(R.id.error_text)).setText(error);
+ }
+ });
+ }
+
+ // Verify we don't start in screen pinning.
+ private final Test mCheckStartedUnpinned = new Test(0) {
+ public void run() {
+ if (mActivityManager.isInLockTaskMode()) {
+ error(R.string.error_screen_already_pinned);
+ } else {
+ succeed();
+ }
+ }
+ };
+
+ // Start screen pinning by having the user click next then confirm it for us.
+ private final Test mCheckStartPinning = new Test(R.string.screen_pin_instructions) {
+ protected void onNextClick() {
+ startLockTask();
+ succeed();
+ }
+ };
+
+ // Click next and check that we got pinned.
+ // Wait for the user to click next to verify that they got back from the prompt
+ // successfully.
+ private final Test mCheckIsPinned = new Test(R.string.screen_pin_check_pinned) {
+ protected void onNextClick() {
+ if (mActivityManager.isInLockTaskMode()) {
+ succeed();
+ } else {
+ error(R.string.error_screen_pinning_did_not_start);
+ }
+ }
+ };
+
+ // Tell user to try to leave.
+ private final Test mCheckTryLeave = new Test(R.string.screen_pin_no_exit) {
+ protected void onNextClick() {
+ if (mActivityManager.isInLockTaskMode()) {
+ succeed();
+ } else {
+ error(R.string.error_screen_no_longer_pinned);
+ }
+ }
+ };
+
+ // Verify that the user unpinned and it worked.
+ private final Test mCheckUnpin = new Test(R.string.screen_pin_exit) {
+ protected void onNextClick() {
+ if (!mActivityManager.isInLockTaskMode()) {
+ succeed();
+ } else {
+ error(R.string.error_screen_pinning_did_not_exit);
+ }
+ }
+ };
+
+ // Unpin from code and check that it worked.
+ private final Test mCheckUnpinFromCode = new Test(0) {
+ protected void run() {
+ if (!mActivityManager.isInLockTaskMode()) {
+ error(R.string.error_screen_pinning_did_not_start);
+ return;
+ }
+ stopLockTask();
+ if (!mActivityManager.isInLockTaskMode()) {
+ succeed();
+ } else {
+ error(R.string.error_screen_pinning_couldnt_exit);
+ }
+ };
+ };
+
+ private final Test mDone = new Test(R.string.screen_pinning_done) {
+ protected void run() {
+ showText();
+ succeed();
+ };
+ };
+
+ private abstract class Test {
+ private final int mResId;
+
+ public Test(int showId) {
+ mResId = showId;
+ }
+
+ protected void run() {
+ showText();
+ }
+
+ public void showText() {
+ if (mResId == 0) {
+ return;
+ }
+ show(mResId);
+ }
+
+ protected void onNextClick() {
+ }
+ }
+
+}
diff --git a/build/test_executable.mk b/build/test_executable.mk
index e0352ba..02b3e4c 100644
--- a/build/test_executable.mk
+++ b/build/test_executable.mk
@@ -43,3 +43,6 @@
-b $(CTS_UNSUPPORTED_ABIS) \
-a $(CTS_TARGET_ARCH) \
-o $@
+
+# Have the module name depend on the cts files; so the cts files get generated when you run mm/mmm/mma/mmma.
+$(my_register_name) : $(cts_executable_xml)
diff --git a/build/test_gtest_package.mk b/build/test_gtest_package.mk
index 6868081..dd1269b 100644
--- a/build/test_gtest_package.mk
+++ b/build/test_gtest_package.mk
@@ -52,3 +52,6 @@
-b $(CTS_UNSUPPORTED_ABIS) \
-a $(CTS_TARGET_ARCH) \
-o $@
+
+# Have the module name depend on the cts files; so the cts files get generated when you run mm/mmm/mma/mmma.
+$(my_register_name) : $(cts_package_apk) $(cts_package_xml)
diff --git a/build/test_host_java_library.mk b/build/test_host_java_library.mk
index 8e071e4..8ed5670 100644
--- a/build/test_host_java_library.mk
+++ b/build/test_host_java_library.mk
@@ -42,3 +42,6 @@
-b $(CTS_UNSUPPORTED_ABIS) \
-a $(CTS_TARGET_ARCH) \
-o $@
+
+# Have the module name depend on the cts files; so the cts files get generated when you run mm/mmm/mma/mmma.
+$(my_register_name) : $(cts_library_xml)
diff --git a/build/test_package.mk b/build/test_package.mk
index 72972b2..7589787 100644
--- a/build/test_package.mk
+++ b/build/test_package.mk
@@ -64,3 +64,6 @@
-b $(CTS_UNSUPPORTED_ABIS) \
-a $(CTS_TARGET_ARCH) \
-o $@
+
+# Have the module name depend on the cts files; so the cts files get generated when you run mm/mmm/mma/mmma.
+$(my_register_name) : $(cts_package_apk) $(cts_package_xml)
diff --git a/build/test_target_java_library.mk b/build/test_target_java_library.mk
index c0d7a2a..04fffb9 100644
--- a/build/test_target_java_library.mk
+++ b/build/test_target_java_library.mk
@@ -44,3 +44,6 @@
-a $(CTS_TARGET_ARCH) \
-x "runtimeArgs->$(PRIVATE_RUNTIME_ARGS)" \
-o $@
+
+# Have the module name depend on the cts files; so the cts files get generated when you run mm/mmm/mma/mmma.
+$(my_register_name) : $(cts_library_jar) $(cts_library_xml)
diff --git a/build/test_uiautomator.mk b/build/test_uiautomator.mk
index 085d672..cad6e4f 100644
--- a/build/test_uiautomator.mk
+++ b/build/test_uiautomator.mk
@@ -55,3 +55,6 @@
-b $(CTS_UNSUPPORTED_ABIS) \
-a $(CTS_TARGET_ARCH) \
-o $@
+
+# Have the module name depend on the cts files; so the cts files get generated when you run mm/mmm/mma/mmma.
+$(my_register_name) : $(cts_library_jar) $(cts_library_xml)
diff --git a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/SplitTests.java b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/SplitTests.java
index caa3e46..90cbed9 100644
--- a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/SplitTests.java
+++ b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/SplitTests.java
@@ -57,6 +57,9 @@
private static final String APK_mips64 = "CtsSplitApp_mips64.apk";
private static final String APK_mips = "CtsSplitApp_mips.apk";
+ private static final String APK_DIFF_REVISION = "CtsSplitAppDiffRevision.apk";
+ private static final String APK_DIFF_REVISION_v7 = "CtsSplitAppDiffRevision_v7.apk";
+
private static final String APK_DIFF_VERSION = "CtsSplitAppDiffVersion.apk";
private static final String APK_DIFF_VERSION_v7 = "CtsSplitAppDiffVersion_v7.apk";
@@ -233,6 +236,30 @@
new InstallMultiple().inheritFrom(PKG).addApk(APK_DIFF_VERSION_v7).runExpectingFailure();
}
+ public void testDiffRevision() throws Exception {
+ new InstallMultiple().addApk(APK).addApk(APK_DIFF_REVISION_v7).run();
+ runDeviceTests(PKG, ".SplitAppTest", "testRevision0_12");
+ }
+
+ public void testDiffRevisionInheritBase() throws Exception {
+ new InstallMultiple().addApk(APK).addApk(APK_v7).run();
+ runDeviceTests(PKG, ".SplitAppTest", "testRevision0_0");
+ new InstallMultiple().inheritFrom(PKG).addApk(APK_DIFF_REVISION_v7).run();
+ runDeviceTests(PKG, ".SplitAppTest", "testRevision0_12");
+ }
+
+ public void testDiffRevisionInheritSplit() throws Exception {
+ new InstallMultiple().addApk(APK).addApk(APK_v7).run();
+ runDeviceTests(PKG, ".SplitAppTest", "testRevision0_0");
+ new InstallMultiple().inheritFrom(PKG).addApk(APK_DIFF_REVISION).run();
+ runDeviceTests(PKG, ".SplitAppTest", "testRevision12_0");
+ }
+
+ public void testDiffRevisionDowngrade() throws Exception {
+ new InstallMultiple().addApk(APK).addApk(APK_DIFF_REVISION_v7).run();
+ new InstallMultiple().inheritFrom(PKG).addApk(APK_v7).runExpectingFailure();
+ }
+
public void testFeatureBase() throws Exception {
new InstallMultiple().addApk(APK).addApk(APK_FEATURE).run();
runDeviceTests(PKG, ".SplitAppTest", "testFeatureBase");
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/Android.mk b/hostsidetests/appsecurity/test-apps/SplitApp/Android.mk
index 8b25f4b..bf89576 100644
--- a/hostsidetests/appsecurity/test-apps/SplitApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/Android.mk
@@ -30,7 +30,31 @@
LOCAL_ASSET_DIR := $(LOCAL_PATH)/assets
LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
-LOCAL_AAPT_FLAGS := --version-code 100 --version-name OneHundred --replace-version -c mdpi -c hdpi -c xhdpi -c xxhdpi
+LOCAL_AAPT_FLAGS := --version-code 100 --version-name OneHundred --replace-version
+
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_PACKAGE)
+
+
+#################################################
+# Define a variant with a different revision code
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsSplitAppDiffRevision
+LOCAL_PACKAGE_SPLITS := v7
+
+LOCAL_MANIFEST_FILE := revision/AndroidManifest.xml
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
+LOCAL_AAPT_FLAGS := --version-code 100 --version-name OneHundredRevisionTwelve --replace-version
LOCAL_PROGUARD_ENABLED := disabled
LOCAL_DEX_PREOPT := false
@@ -53,7 +77,7 @@
LOCAL_PACKAGE_SPLITS := v7
LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
-LOCAL_AAPT_FLAGS := --version-code 101 --version-name OneHundredOne --replace-version -c mdpi -c hdpi -c xhdpi -c xxhdpi
+LOCAL_AAPT_FLAGS := --version-code 101 --version-name OneHundredOne --replace-version
LOCAL_PROGUARD_ENABLED := disabled
LOCAL_DEX_PREOPT := false
@@ -76,7 +100,7 @@
LOCAL_PACKAGE_SPLITS := v7
LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey2
-LOCAL_AAPT_FLAGS := --version-code 100 --version-name OneHundred --replace-version -c mdpi -c hdpi -c xhdpi -c xxhdpi
+LOCAL_AAPT_FLAGS := --version-code 100 --version-name OneHundred --replace-version
LOCAL_PROGUARD_ENABLED := disabled
LOCAL_DEX_PREOPT := false
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature/Android.mk b/hostsidetests/appsecurity/test-apps/SplitApp/feature/Android.mk
index e93f6c3..809a6b8 100644
--- a/hostsidetests/appsecurity/test-apps/SplitApp/feature/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/feature/Android.mk
@@ -24,7 +24,7 @@
LOCAL_ASSET_DIR := $(LOCAL_PATH)/assets
LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
-LOCAL_AAPT_FLAGS := --version-code 100 --version-name OneHundred --replace-version -c mdpi -c hdpi -c xhdpi -c xxhdpi
+LOCAL_AAPT_FLAGS := --version-code 100 --version-name OneHundred --replace-version
LOCAL_MODULE_TAGS := tests
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/revision/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/SplitApp/revision/AndroidManifest.xml
new file mode 100644
index 0000000..8e053ba
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/revision/AndroidManifest.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.cts.splitapp"
+ android:revisionCode="12">
+
+ <uses-permission android:name="android.permission.CAMERA" />
+
+ <application android:label="SplitApp">
+ <activity android:name=".MyActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ <meta-data android:name="android.service.wallpaper" android:resource="@xml/my_activity_meta" />
+ </activity>
+ <receiver android:name=".MyReceiver"
+ android:enabled="@bool/my_receiver_enabled">
+ <intent-filter>
+ <action android:name="android.intent.action.DATE_CHANGED" />
+ </intent-filter>
+ </receiver>
+
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.cts.splitapp" />
+
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/SplitAppTest.java b/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/SplitAppTest.java
index 5046458..3d6cee7 100644
--- a/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/SplitAppTest.java
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/SplitAppTest.java
@@ -21,6 +21,7 @@
import android.content.Intent;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
@@ -30,6 +31,7 @@
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.test.AndroidTestCase;
+import android.test.MoreAsserts;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -319,6 +321,30 @@
assertFalse(new File(getContext().getCodeCacheDir(), "cache.raw").exists());
}
+ public void testRevision0_0() throws Exception {
+ final PackageInfo info = getContext().getPackageManager()
+ .getPackageInfo(getContext().getPackageName(), 0);
+ assertEquals(0, info.baseRevisionCode);
+ assertEquals(1, info.splitRevisionCodes.length);
+ assertEquals(0, info.splitRevisionCodes[0]);
+ }
+
+ public void testRevision12_0() throws Exception {
+ final PackageInfo info = getContext().getPackageManager()
+ .getPackageInfo(getContext().getPackageName(), 0);
+ assertEquals(12, info.baseRevisionCode);
+ assertEquals(1, info.splitRevisionCodes.length);
+ assertEquals(0, info.splitRevisionCodes[0]);
+ }
+
+ public void testRevision0_12() throws Exception {
+ final PackageInfo info = getContext().getPackageManager()
+ .getPackageInfo(getContext().getPackageName(), 0);
+ assertEquals(0, info.baseRevisionCode);
+ assertEquals(1, info.splitRevisionCodes.length);
+ assertEquals(12, info.splitRevisionCodes[0]);
+ }
+
private static void updateDpi(Resources r, int densityDpi) {
final Configuration c = new Configuration(r.getConfiguration());
c.densityDpi = densityDpi;
diff --git a/hostsidetests/theme/src/android/theme/cts/ComparisonTask.java b/hostsidetests/theme/src/android/theme/cts/ComparisonTask.java
index 33e67e5..ba880d7 100644
--- a/hostsidetests/theme/src/android/theme/cts/ComparisonTask.java
+++ b/hostsidetests/theme/src/android/theme/cts/ComparisonTask.java
@@ -37,9 +37,7 @@
private static final int IMAGE_THRESHOLD = 2;
- private static final String STORAGE_PATH_DEVICE = "/storage/emulated/legacy/cts-holo-assets/%s.png";
-
- private static final String STORAGE_PATH_EMULATOR = "/sdcard/cts-holo-assets/%s.png";
+ private static final String STORAGE_PATH_DEVICE = "/sdcard/cts-holo-assets/%s.png";
private final ITestDevice mDevice;
@@ -47,18 +45,10 @@
private final String mName;
- private final String mStoragePath;
-
public ComparisonTask(ITestDevice device, File reference, String name) {
mDevice = device;
mReference = reference;
mName = name;
-
- if (mDevice.getSerialNumber().startsWith("emulator-")) {
- mStoragePath = STORAGE_PATH_EMULATOR;
- } else {
- mStoragePath = STORAGE_PATH_DEVICE;
- }
}
public Boolean call() {
@@ -67,7 +57,7 @@
try {
generated = File.createTempFile("gen_" + mName, ".png");
- final String remoteGenerated = String.format(mStoragePath, mName);
+ final String remoteGenerated = String.format(STORAGE_PATH_DEVICE, mName);
if (!mDevice.doesFileExist(remoteGenerated)) {
Log.logAndDisplay(LogLevel.ERROR, TAG, "File " + remoteGenerated + " have not been saved on device");
return false;
@@ -84,7 +74,7 @@
Log.logAndDisplay(LogLevel.INFO, TAG, "Diff created: " + diff.getPath());
}
} catch (Exception e) {
- Log.logAndDisplay(LogLevel.ERROR, TAG, String.format(mStoragePath, mName));
+ Log.logAndDisplay(LogLevel.ERROR, TAG, String.format(STORAGE_PATH_DEVICE, mName));
Log.logAndDisplay(LogLevel.ERROR, TAG, e.toString());
e.printStackTrace();
} finally {
diff --git a/tests/JobScheduler/src/android/jobscheduler/MockJobService.java b/tests/JobScheduler/src/android/jobscheduler/MockJobService.java
index a0177e2..38a753d 100644
--- a/tests/JobScheduler/src/android/jobscheduler/MockJobService.java
+++ b/tests/JobScheduler/src/android/jobscheduler/MockJobService.java
@@ -34,7 +34,7 @@
private static final String TAG = "MockJobService";
/** Wait this long before timing out the test. */
- private static final long DEFAULT_TIMEOUT_MILLIS = 5000L; // 5 seconds.
+ private static final long DEFAULT_TIMEOUT_MILLIS = 30000L; // 30 seconds.
@Override
public void onCreate() {
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/TimingConstraintsTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/TimingConstraintsTest.java
index 36f44ef..ed9cadd 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/TimingConstraintsTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/TimingConstraintsTest.java
@@ -29,7 +29,7 @@
public void testScheduleOnce() throws Exception {
JobInfo oneTimeJob = new JobInfo.Builder(TIMING_JOB_ID, kJobServiceComponent)
- .setOverrideDeadline(1000) // 1 secs
+ .setOverrideDeadline(5000) // 5 secs
.build();
kTestEnvironment.setExpectedExecutions(1);
@@ -41,7 +41,7 @@
public void testSchedulePeriodic() throws Exception {
JobInfo periodicJob =
new JobInfo.Builder(TIMING_JOB_ID, kJobServiceComponent)
- .setPeriodic(1000L) // 1 second period.
+ .setPeriodic(5000L) // 5 second period.
.build();
kTestEnvironment.setExpectedExecutions(3);
@@ -52,7 +52,8 @@
public void testCancel() throws Exception {
JobInfo cancelJob = new JobInfo.Builder(CANCEL_JOB_ID, kJobServiceComponent)
- .setOverrideDeadline(2000L)
+ .setMinimumLatency(5000L) // make sure it doesn't actually run immediately
+ .setOverrideDeadline(7000L)
.build();
kTestEnvironment.setExpectedExecutions(0);
diff --git a/tests/expectations/knownfailures.txt b/tests/expectations/knownfailures.txt
index 7532fc8..7c500b9 100644
--- a/tests/expectations/knownfailures.txt
+++ b/tests/expectations/knownfailures.txt
@@ -133,35 +133,10 @@
{
description: "New tests recently added for Android Enterprise. To be moved out of CTS-staging as soon as they show that they are stable",
names: [
- "com.android.cts.devicepolicy.DeviceOwnerTest#testApplicationRestrictions",
- "com.android.cts.devicepolicy.DeviceOwnerTest#testCaCertManagement",
- "com.android.cts.devicepolicy.DeviceOwnerTest#testDeviceOwnerSetup",
- "com.android.cts.devicepolicy.DeviceOwnerTest#testPersistentIntentResolving",
- "com.android.cts.devicepolicy.DeviceOwnerTest#testScreenCaptureDisabled",
- "com.android.cts.devicepolicy.ManagedProfileTest#testManagedProfileSetup",
- "com.android.cts.devicepolicy.ManagedProfileTest#testWipeData",
- "com.android.cts.devicepolicy.ManagedProfileTest#testCrossProfileIntentFilters",
- "com.android.cts.devicepolicy.ManagedProfileTest#testCrossProfileContent",
- "com.android.cts.devicepolicy.ManagedProfileTest#testNoDebuggingFeaturesRestriction",
- "com.android.cts.devicepolicy.ManagedProfileTest#testCrossProfileCopyPaste",
- "com.android.cts.devicepolicy.DeviceOwnerTest#testLockTask"
+ "com.android.cts.devicepolicy.ManagedProfileTest#testCrossProfileCopyPaste"
]
},
{
-
- description: "These tests fail on some devices.",
- names: [
- "android.uirendering.cts.testclasses.ExactCanvasTests#testBlueRect",
- "android.uirendering.cts.testclasses.ExactCanvasTests#testBluePaddedSquare",
- "android.uirendering.cts.testclasses.ViewClippingTests#testSimplePaddingClip",
- "android.uirendering.cts.testclasses.ViewClippingTests#testSimpleClipBoundsClip",
- "android.uirendering.cts.testclasses.ViewClippingTests#testSimpleOutlineClip",
- "android.uirendering.cts.testclasses.ViewClippingTests#testSimpleBoundsClip",
- "android.uirendering.cts.testclasses.InfrastructureTests#testViewInitializer"
- ],
- bug: 17511118
-},
-{
description: "This test failed on devices that use effect off loading. In addition it uses hidden apis",
names: [
"android.media.cts.AudioEffectTest#test1_1ConstructorFromUuid"
diff --git a/tests/tests/app/src/android/app/cts/AlarmManagerTest.java b/tests/tests/app/src/android/app/cts/AlarmManagerTest.java
index bfc3e1d..b85c616 100644
--- a/tests/tests/app/src/android/app/cts/AlarmManagerTest.java
+++ b/tests/tests/app/src/android/app/cts/AlarmManagerTest.java
@@ -18,14 +18,17 @@
import android.app.AlarmManager;
+import android.app.AlarmManager.AlarmClockInfo;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.cts.util.PollingCheck;
+import android.os.Build;
import android.os.SystemClock;
import android.test.AndroidTestCase;
import android.util.Log;
+import android.test.MoreAsserts;
public class AlarmManagerTest extends AndroidTestCase {
public static final String MOCKACTION = "android.app.AlarmManagerTest.TEST_ALARMRECEIVER";
@@ -47,6 +50,7 @@
private static final int TIME_DELTA = 1000;
private static final int TIME_DELAY = 10000;
+ private static final int REPEAT_PERIOD = 60000;
// Receiver registration/unregistration between tests races with the system process, so
// we add a little buffer time here to allow the system to process before we proceed.
@@ -221,46 +225,54 @@
public void testSetRepeating() throws Exception {
mMockAlarmReceiver.setAlarmedFalse();
- mWakeupTime = System.currentTimeMillis() + SNOOZE_DELAY;
- mAm.setRepeating(AlarmManager.RTC_WAKEUP, mWakeupTime, TIME_DELAY / 2, mSender);
- new PollingCheck(SNOOZE_DELAY + TIME_DELAY) {
+ mWakeupTime = System.currentTimeMillis() + TEST_ALARM_FUTURITY;
+ mAm.setRepeating(AlarmManager.RTC_WAKEUP, mWakeupTime, REPEAT_PERIOD, mSender);
+
+ // wait beyond the initial alarm's possible delivery window to verify that it fires the first time
+ new PollingCheck(TEST_ALARM_FUTURITY + REPEAT_PERIOD) {
@Override
protected boolean check() {
return mMockAlarmReceiver.alarmed;
}
}.run();
+ assertTrue(mMockAlarmReceiver.alarmed);
+
+ // Now reset the receiver and wait for the intended repeat alarm to fire as expected
mMockAlarmReceiver.setAlarmedFalse();
- new PollingCheck(TIME_DELAY) {
+ new PollingCheck(REPEAT_PERIOD*2) {
@Override
protected boolean check() {
return mMockAlarmReceiver.alarmed;
}
}.run();
+ assertTrue(mMockAlarmReceiver.alarmed);
+
mAm.cancel(mSender);
}
public void testCancel() throws Exception {
mMockAlarmReceiver.setAlarmedFalse();
- mWakeupTime = System.currentTimeMillis() + SNOOZE_DELAY;
- mAm.setRepeating(AlarmManager.RTC_WAKEUP, mWakeupTime, 1000, mSender);
- new PollingCheck(SNOOZE_DELAY + TIME_DELAY) {
- @Override
- protected boolean check() {
- return mMockAlarmReceiver.alarmed;
- }
- }.run();
- mMockAlarmReceiver.setAlarmedFalse();
+ mMockAlarmReceiver2.setAlarmedFalse();
+
+ // set two alarms
+ final long when1 = System.currentTimeMillis() + TEST_ALARM_FUTURITY;
+ mAm.setExact(AlarmManager.RTC_WAKEUP, when1, mSender);
+ final long when2 = when1 + TIME_DELTA; // will fire after when1's target time
+ mAm.setExact(AlarmManager.RTC_WAKEUP, when2, mSender2);
+
+ // cancel the earlier one
+ mAm.cancel(mSender);
+
+ // and verify that only the later one fired
new PollingCheck(TIME_DELAY) {
@Override
protected boolean check() {
- return mMockAlarmReceiver.alarmed;
+ return mMockAlarmReceiver2.alarmed;
}
}.run();
- mAm.cancel(mSender);
- Thread.sleep(TIME_DELAY);
- mMockAlarmReceiver.setAlarmedFalse();
- Thread.sleep(TIME_DELAY * 5);
+
assertFalse(mMockAlarmReceiver.alarmed);
+ assertTrue(mMockAlarmReceiver2.alarmed);
}
public void testSetInexactRepeating() throws Exception {
@@ -273,4 +285,66 @@
// " Unable to open alarm driver: Permission denied". But still fail
// after tried many permission.
}
+
+ public void testSetAlarmClock() throws Exception {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ mMockAlarmReceiver.setAlarmedFalse();
+ mMockAlarmReceiver2.setAlarmedFalse();
+
+ // Set first alarm clock.
+ final long wakeupTimeFirst = System.currentTimeMillis()
+ + 2 * TEST_ALARM_FUTURITY;
+ mAm.setAlarmClock(new AlarmClockInfo(wakeupTimeFirst, null), mSender);
+
+ // Verify getNextAlarmClock returns first alarm clock.
+ AlarmClockInfo nextAlarmClock = mAm.getNextAlarmClock();
+ assertEquals(wakeupTimeFirst, nextAlarmClock.getTriggerTime());
+ assertNull(nextAlarmClock.getShowIntent());
+
+ // Set second alarm clock, earlier than first.
+ final long wakeupTimeSecond = System.currentTimeMillis()
+ + TEST_ALARM_FUTURITY;
+ PendingIntent showIntentSecond = PendingIntent.getBroadcast(getContext(), 0,
+ new Intent(getContext(), AlarmManagerTest.class).setAction("SHOW_INTENT"), 0);
+ mAm.setAlarmClock(new AlarmClockInfo(wakeupTimeSecond, showIntentSecond),
+ mSender2);
+
+ // Verify getNextAlarmClock returns second alarm clock now.
+ nextAlarmClock = mAm.getNextAlarmClock();
+ assertEquals(wakeupTimeSecond, nextAlarmClock.getTriggerTime());
+ assertEquals(showIntentSecond, nextAlarmClock.getShowIntent());
+
+ // Cancel second alarm.
+ mAm.cancel(mSender2);
+
+ // Verify getNextAlarmClock returns first alarm clock again.
+ nextAlarmClock = mAm.getNextAlarmClock();
+ assertEquals(wakeupTimeFirst, nextAlarmClock.getTriggerTime());
+ assertNull(nextAlarmClock.getShowIntent());
+
+ // Wait for first alarm to trigger.
+ assertFalse(mMockAlarmReceiver.alarmed);
+ new PollingCheck(2 * TEST_ALARM_FUTURITY + TIME_DELAY) {
+ @Override
+ protected boolean check() {
+ return mMockAlarmReceiver.alarmed;
+ }
+ }.run();
+
+ // Verify first alarm fired at the right time.
+ assertEquals(mMockAlarmReceiver.rtcTime, wakeupTimeFirst, TIME_DELTA);
+
+ // Verify second alarm didn't fire.
+ assertFalse(mMockAlarmReceiver2.alarmed);
+
+ // Verify the next alarm is not returning neither the first nor the second alarm.
+ nextAlarmClock = mAm.getNextAlarmClock();
+ MoreAsserts.assertNotEqual(wakeupTimeFirst, nextAlarmClock != null
+ ? nextAlarmClock.getTriggerTime()
+ : null);
+ MoreAsserts.assertNotEqual(wakeupTimeSecond, nextAlarmClock != null
+ ? nextAlarmClock.getTriggerTime()
+ : null);
+ }
+ }
}
diff --git a/tests/tests/app/src/android/app/cts/DownloadManagerTest.java b/tests/tests/app/src/android/app/cts/DownloadManagerTest.java
index 84faffa..a68d860 100644
--- a/tests/tests/app/src/android/app/cts/DownloadManagerTest.java
+++ b/tests/tests/app/src/android/app/cts/DownloadManagerTest.java
@@ -49,7 +49,7 @@
private static final int MINIMUM_DOWNLOAD_BYTES = 100 * 1024 * 1024;
private static final long SHORT_TIMEOUT = 5 * DateUtils.SECOND_IN_MILLIS;
- private static final long LONG_TIMEOUT = 2 * DateUtils.MINUTE_IN_MILLIS;
+ private static final long LONG_TIMEOUT = 3 * DateUtils.MINUTE_IN_MILLIS;
private DownloadManager mDownloadManager;
diff --git a/tests/tests/app/src/android/app/cts/InstrumentationTest.java b/tests/tests/app/src/android/app/cts/InstrumentationTest.java
index 0c2e9fa..b21148e 100644
--- a/tests/tests/app/src/android/app/cts/InstrumentationTest.java
+++ b/tests/tests/app/src/android/app/cts/InstrumentationTest.java
@@ -196,10 +196,12 @@
public void testInvokeMenuActionSync() throws Exception {
final int resId = R.id.goto_menu_id;
- mInstrumentation.invokeMenuActionSync(mActivity, resId, 0);
- mInstrumentation.waitForIdleSync();
-
- assertEquals(resId, mActivity.getMenuID());
+ if (mActivity.getWindow().hasFeature(Window.FEATURE_OPTIONS_PANEL)) {
+ mInstrumentation.invokeMenuActionSync(mActivity, resId, 0);
+ mInstrumentation.waitForIdleSync();
+
+ assertEquals(resId, mActivity.getMenuID());
+ }
}
public void testCallActivityOnPostCreate() throws Throwable {
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
index dd17779..872f951 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -447,22 +447,26 @@
assertTrue("rowStride " + rowStride + " should be >= width " + w , rowStride >= w);
for (int row = 0; row < h; row++) {
int bytesPerPixel = ImageFormat.getBitsPerPixel(format) / 8;
+ int length;
if (pixelStride == bytesPerPixel) {
// Special case: optimized read of the entire row
- int length = w * bytesPerPixel;
+ length = w * bytesPerPixel;
buffer.get(data, offset, length);
- // Advance buffer the remainder of the row stride
- buffer.position(buffer.position() + rowStride - length);
offset += length;
} else {
// Generic case: should work for any pixelStride but slower.
// Use intermediate buffer to avoid read byte-by-byte from
// DirectByteBuffer, which is very bad for performance
- buffer.get(rowData, 0, rowStride);
+ length = (w - 1) * pixelStride + bytesPerPixel;
+ buffer.get(rowData, 0, length);
for (int col = 0; col < w; col++) {
data[offset++] = rowData[col * pixelStride];
}
}
+ // Advance buffer the remainder of the row stride
+ if (row < h - 1) {
+ buffer.position(buffer.position() + rowStride - length);
+ }
}
if (VERBOSE) Log.v(TAG, "Finished reading data from plane " + i);
buffer.rewind();
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/DngCreatorTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/DngCreatorTest.java
index 9ec649e..d5972e2 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/DngCreatorTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/DngCreatorTest.java
@@ -16,15 +16,22 @@
package android.hardware.camera2.cts;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapRegionDecoder;
import android.graphics.ImageFormat;
import android.graphics.Rect;
-import android.hardware.camera2.CameraCaptureSession;
+import android.graphics.RectF;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.DngCreator;
import android.hardware.camera2.cts.helpers.StaticMetadata;
+import android.hardware.camera2.cts.rs.BitmapUtils;
+import android.hardware.camera2.cts.rs.RawConverter;
+import android.hardware.camera2.cts.rs.RenderScriptSingleton;
import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
import android.location.Location;
import android.media.ExifInterface;
@@ -37,11 +44,14 @@
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
+import java.nio.channels.FileChannel;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
-import static android.hardware.camera2.cts.CameraTestUtils.configureCameraSession;
import static android.hardware.camera2.cts.helpers.AssertHelpers.*;
+import static junit.framework.Assert.assertTrue;
/**
* Tests for the DngCreator API.
@@ -51,6 +61,9 @@
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
private static final String DEBUG_DNG_FILE = "raw16.dng";
+ private static final double IMAGE_DIFFERENCE_TOLERANCE = 60;
+ private static final int DEFAULT_PATCH_DIMEN = 512;
+
@Override
protected void setUp() throws Exception {
super.setUp();
@@ -61,6 +74,13 @@
super.tearDown();
}
+ @Override
+ public synchronized void setContext(Context context) {
+ super.setContext(context);
+
+ RenderScriptSingleton.setContext(context);
+ }
+
/**
* Test basic raw capture and DNG saving functionality for each of the available cameras.
*
@@ -120,14 +140,15 @@
dngCreator.writeImage(outputStream, resultPair.first);
if (VERBOSE) {
- String filePath = DEBUG_FILE_NAME_BASE + "camera_" + deviceId + "_" +
+ // Write DNG to file
+ String dngFilePath = DEBUG_FILE_NAME_BASE + "/camera_basic_" + deviceId + "_" +
DEBUG_DNG_FILE;
// Write out captured DNG file for the first camera device if setprop is enabled
- fileStream = new FileOutputStream(filePath);
+ fileStream = new FileOutputStream(dngFilePath);
fileStream.write(outputStream.toByteArray());
fileStream.flush();
fileStream.close();
- Log.v(TAG, "Test DNG file for camera " + deviceId + " saved to " + filePath);
+ Log.v(TAG, "Test DNG file for camera " + deviceId + " saved to " + dngFilePath);
}
} finally {
closeDevice(deviceId);
@@ -231,7 +252,7 @@
dngCreator.writeImage(outputStream, resultPair.first.get(0));
if (VERBOSE) {
- String filePath = DEBUG_FILE_NAME_BASE + "camera_" + deviceId + "_" +
+ String filePath = DEBUG_FILE_NAME_BASE + "/camera_thumb_" + deviceId + "_" +
DEBUG_DNG_FILE;
// Write out captured DNG file for the first camera device if setprop is enabled
fileStream = new FileOutputStream(filePath);
@@ -257,6 +278,222 @@
}
}
+ /**
+ * Test basic RAW capture, and ensure that the rendered RAW output is similar to the JPEG
+ * created for the same frame.
+ *
+ * <p>
+ * This test renders the RAW buffer into an RGB bitmap using a rendering pipeline
+ * similar to one in the Adobe DNG validation tool. JPEGs produced by the vendor hardware may
+ * have different tonemapping and saturation applied than the RGB bitmaps produced
+ * from this DNG rendering pipeline, and this test allows for fairly wide variations
+ * between the histograms for the RAW and JPEG buffers to avoid false positives.
+ * </p>
+ *
+ * <p>
+ * To ensure more subtle errors in the colorspace transforms returned for the HAL's RAW
+ * metadata, the DNGs and JPEGs produced here should also be manually compared using external
+ * DNG rendering tools. The DNG, rendered RGB bitmap, and JPEG buffer for this test can be
+ * dumped to the SD card for further examination by enabling the 'verbose' mode for this test
+ * using:
+ * adb shell setprop log.tag.DngCreatorTest VERBOSE
+ * </p>
+ */
+ public void testRaw16JpegConsistency() throws Exception {
+ for (int i = 0; i < mCameraIds.length; i++) {
+ String deviceId = mCameraIds[i];
+ List<ImageReader> captureReaders = new ArrayList<ImageReader>();
+ List<CameraTestUtils.SimpleImageReaderListener> captureListeners =
+ new ArrayList<CameraTestUtils.SimpleImageReaderListener>();
+ FileOutputStream fileStream = null;
+ ByteArrayOutputStream outputStream = null;
+ FileChannel fileChannel = null;
+ try {
+ openDevice(deviceId);
+
+ if (!mStaticInfo.isCapabilitySupported(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
+ Log.i(TAG, "RAW capability is not supported in camera " + mCameraIds[i] +
+ ". Skip the test.");
+ continue;
+ }
+
+ Size[] targetCaptureSizes =
+ mStaticInfo.getAvailableSizesForFormatChecked(ImageFormat.RAW_SENSOR,
+ StaticMetadata.StreamDirection.Output);
+
+ assertTrue("No capture sizes available for RAW format!",
+ targetCaptureSizes.length != 0);
+ Rect activeArray = mStaticInfo.getActiveArraySizeChecked();
+ Size activeArraySize = new Size(activeArray.width(), activeArray.height());
+ assertTrue("Active array has invalid size!", activeArray.width() > 0 &&
+ activeArray.height() > 0);
+ // TODO: Allow PixelArraySize also.
+ assertArrayContains("Available sizes for RAW format must include ActiveArraySize",
+ targetCaptureSizes, activeArraySize);
+
+ // Get largest jpeg size
+ Size[] targetJpegSizes =
+ mStaticInfo.getAvailableSizesForFormatChecked(ImageFormat.JPEG,
+ StaticMetadata.StreamDirection.Output);
+
+ Size largestJpegSize = Collections.max(Arrays.asList(targetJpegSizes),
+ new CameraTestUtils.SizeComparator());
+
+ // Create raw image reader and capture listener
+ CameraTestUtils.SimpleImageReaderListener rawListener
+ = new CameraTestUtils.SimpleImageReaderListener();
+ captureReaders.add(createImageReader(activeArraySize, ImageFormat.RAW_SENSOR, 2,
+ rawListener));
+ captureListeners.add(rawListener);
+
+
+ // Create jpeg image reader and capture listener
+ CameraTestUtils.SimpleImageReaderListener jpegListener
+ = new CameraTestUtils.SimpleImageReaderListener();
+ captureReaders.add(createImageReader(largestJpegSize, ImageFormat.JPEG, 2,
+ jpegListener));
+ captureListeners.add(jpegListener);
+
+ Pair<List<Image>, CaptureResult> resultPair = captureSingleRawShot(activeArraySize,
+ captureReaders, captureListeners);
+ CameraCharacteristics characteristics = mStaticInfo.getCharacteristics();
+ Image raw = resultPair.first.get(0);
+ Image jpeg = resultPair.first.get(1);
+
+ Bitmap rawBitmap = Bitmap.createBitmap(raw.getWidth(), raw.getHeight(),
+ Bitmap.Config.ARGB_8888);
+ byte[] rawPlane = new byte[raw.getPlanes()[0].getRowStride() * raw.getHeight()];
+
+ // Render RAW image to a bitmap
+ raw.getPlanes()[0].getBuffer().get(rawPlane);
+ raw.getPlanes()[0].getBuffer().rewind();
+ RawConverter.convertToSRGB(RenderScriptSingleton.getRS(), raw.getWidth(),
+ raw.getHeight(), rawPlane, characteristics,
+ resultPair.second, /*offsetX*/0, /*offsetY*/0, /*out*/rawBitmap);
+
+ // Decompress JPEG image to a bitmap
+ byte[] compressedJpegData = CameraTestUtils.getDataFromImage(jpeg);
+
+ BitmapFactory.Options opt = new BitmapFactory.Options();
+ opt.inPreferredConfig = Bitmap.Config.ARGB_8888;
+ Bitmap fullSizeJpegBmap = BitmapFactory.decodeByteArray(compressedJpegData,
+ /*offset*/0, compressedJpegData.length, /*inout*/opt);
+ Rect jpegDimens = new Rect(0, 0, fullSizeJpegBmap.getWidth(),
+ fullSizeJpegBmap.getHeight());
+
+ if (VERBOSE) {
+ // Generate DNG file
+ DngCreator dngCreator = new DngCreator(characteristics, resultPair.second);
+ outputStream = new ByteArrayOutputStream();
+ dngCreator.writeImage(outputStream, raw);
+
+ // Write DNG to file
+ String dngFilePath = DEBUG_FILE_NAME_BASE + "/camera_" + deviceId + "_" +
+ DEBUG_DNG_FILE;
+ // Write out captured DNG file for the first camera device if setprop is enabled
+ fileStream = new FileOutputStream(dngFilePath);
+ fileStream.write(outputStream.toByteArray());
+ fileStream.flush();
+ fileStream.close();
+ Log.v(TAG, "Test DNG file for camera " + deviceId + " saved to " + dngFilePath);
+
+ // Write JPEG to file
+ String jpegFilePath = DEBUG_FILE_NAME_BASE + "/camera_" + deviceId + "_jpeg.jpg";
+ // Write out captured DNG file for the first camera device if setprop is enabled
+ fileChannel = new FileOutputStream(jpegFilePath).getChannel();
+ fileChannel.write(jpeg.getPlanes()[0].getBuffer());
+ fileChannel.close();
+ Log.v(TAG, "Test JPEG file for camera " + deviceId + " saved to " +
+ jpegFilePath);
+
+ // Write jpeg generated from demosaiced RAW frame to file
+ String rawFilePath = DEBUG_FILE_NAME_BASE + "/camera_" + deviceId + "_raw.jpg";
+ // Write out captured DNG file for the first camera device if setprop is enabled
+ fileStream = new FileOutputStream(rawFilePath);
+ rawBitmap.compress(Bitmap.CompressFormat.JPEG, 90, fileStream);
+ fileStream.flush();
+ fileStream.close();
+ Log.v(TAG, "Test converted RAW file for camera " + deviceId + " saved to " +
+ rawFilePath);
+ }
+
+ Size rawBitmapSize = new Size(rawBitmap.getWidth(), rawBitmap.getHeight());
+ assertTrue("Raw bitmap size must be equal to active array size.",
+ rawBitmapSize.equals(activeArraySize));
+
+ // Get square center patch from JPEG and RAW bitmaps
+ RectF jpegRect = new RectF(jpegDimens);
+ RectF rawRect = new RectF(0, 0, rawBitmap.getWidth(), rawBitmap.getHeight());
+ int sideDimen = Math.min(Math.min(Math.min(Math.min(DEFAULT_PATCH_DIMEN,
+ jpegDimens.width()), jpegDimens.height()), rawBitmap.getWidth()),
+ rawBitmap.getHeight());
+
+ RectF jpegIntermediate = new RectF(0, 0, sideDimen, sideDimen);
+ jpegIntermediate.offset(jpegRect.centerX() - jpegIntermediate.centerX(),
+ jpegRect.centerY() - jpegIntermediate.centerY());
+ RectF rawIntermediate = new RectF(0, 0, sideDimen, sideDimen);
+ rawIntermediate.offset(rawRect.centerX() - rawIntermediate.centerX(),
+ rawRect.centerY() - rawIntermediate.centerY());
+ Rect jpegFinal = new Rect();
+ jpegIntermediate.roundOut(jpegFinal);
+ Rect rawFinal = new Rect();
+ rawIntermediate.roundOut(rawFinal);
+
+ Bitmap jpegPatch = Bitmap.createBitmap(fullSizeJpegBmap, jpegFinal.left,
+ jpegFinal.top, jpegFinal.width(), jpegFinal.height());
+ Bitmap rawPatch = Bitmap.createBitmap(rawBitmap, rawFinal.left, rawFinal.top,
+ rawFinal.width(), rawFinal.height());
+
+ // Compare center patch from JPEG and rendered RAW bitmap
+ double difference = BitmapUtils.calcDifferenceMetric(jpegPatch, rawPatch);
+ if (difference > IMAGE_DIFFERENCE_TOLERANCE) {
+ // Write JPEG patch to file
+ String jpegFilePath = DEBUG_FILE_NAME_BASE + "/camera_" + deviceId +
+ "_jpeg_patch.jpg";
+ fileStream = new FileOutputStream(jpegFilePath);
+ jpegPatch.compress(Bitmap.CompressFormat.JPEG, 90, fileStream);
+ fileStream.flush();
+ fileStream.close();
+ Log.e(TAG, "Failed JPEG patch file for camera " + deviceId + " saved to " +
+ jpegFilePath);
+
+ // Write RAW patch to file
+ String rawFilePath = DEBUG_FILE_NAME_BASE + "/camera_" + deviceId +
+ "_raw_patch.jpg";
+ fileStream = new FileOutputStream(rawFilePath);
+ rawPatch.compress(Bitmap.CompressFormat.JPEG, 90, fileStream);
+ fileStream.flush();
+ fileStream.close();
+ Log.e(TAG, "Failed RAW patch file for camera " + deviceId + " saved to " +
+ rawFilePath);
+
+ fail("Camera " + mCamera.getId() + ": RAW and JPEG image at for the same " +
+ "frame are not similar, center patches have difference metric of " +
+ difference);
+ }
+
+ } finally {
+ closeDevice(deviceId);
+ for (ImageReader r : captureReaders) {
+ closeImageReader(r);
+ }
+
+ if (fileChannel != null) {
+ fileChannel.close();
+ }
+
+ if (outputStream != null) {
+ outputStream.close();
+ }
+
+ if (fileStream != null) {
+ fileStream.close();
+ }
+ }
+ }
+ }
+
private Pair<Image, CaptureResult> captureSingleRawShot(Size s, ImageReader captureReader,
CameraTestUtils.SimpleImageReaderListener captureListener) throws Exception {
List<ImageReader> readers = new ArrayList<ImageReader>();
@@ -268,22 +505,27 @@
return new Pair<Image, CaptureResult>(res.first.get(0), res.second);
}
+ private Pair<List<Image>, CaptureResult> captureSingleRawShot(Size s, List<ImageReader> captureReaders,
+ List<CameraTestUtils.SimpleImageReaderListener> captureListeners) throws Exception {
+ return captureRawShots(s, captureReaders, captureListeners, 1).get(0);
+ }
+
/**
- * Capture a single raw image.
+ * Capture raw images.
*
- * <p>Capture an raw image for a given size.</p>
+ * <p>Capture raw images for a given size.</p>
*
* @param s The size of the raw image to capture. Must be one of the available sizes for this
* device.
- * @return a pair containing the {@link Image} and {@link CaptureResult} used for this capture.
+ * @return a list of pairs containing a {@link Image} and {@link CaptureResult} used for
+ * each capture.
*/
- private Pair<List<Image>, CaptureResult> captureSingleRawShot(Size s, List<ImageReader> captureReaders,
- List<CameraTestUtils.SimpleImageReaderListener> captureListeners) throws Exception {
+ private List<Pair<List<Image>, CaptureResult>> captureRawShots(Size s, List<ImageReader> captureReaders,
+ List<CameraTestUtils.SimpleImageReaderListener> captureListeners, int numShots) throws Exception {
if (VERBOSE) {
Log.v(TAG, "captureSingleRawShot - Capturing raw image.");
}
- Size maxYuvSz = mOrderedPreviewSizes.get(0);
Size[] targetCaptureSizes =
mStaticInfo.getAvailableSizesForFormatChecked(ImageFormat.RAW_SENSOR,
StaticMetadata.StreamDirection.Output);
@@ -312,23 +554,29 @@
CameraTestUtils.SimpleCaptureCallback resultListener =
new CameraTestUtils.SimpleCaptureCallback();
- startCapture(request.build(), /*repeating*/false, resultListener, mHandler);
+ CaptureRequest request1 = request.build();
+ for (int i = 0; i < numShots; i++) {
+ startCapture(request1, /*repeating*/false, resultListener, mHandler);
+ }
+ List<Pair<List<Image>, CaptureResult>> ret = new ArrayList<>();
+ for (int i = 0; i < numShots; i++) {
+ // Verify capture result and images
+ CaptureResult result = resultListener.getCaptureResult(CAPTURE_WAIT_TIMEOUT_MS);
- // Verify capture result and images
- CaptureResult result = resultListener.getCaptureResult(CAPTURE_WAIT_TIMEOUT_MS);
-
- List<Image> resultImages = new ArrayList<Image>();
- for (CameraTestUtils.SimpleImageReaderListener captureListener : captureListeners) {
- Image captureImage = captureListener.getImage(CAPTURE_WAIT_TIMEOUT_MS);
+ List<Image> resultImages = new ArrayList<Image>();
+ for (CameraTestUtils.SimpleImageReaderListener captureListener : captureListeners) {
+ Image captureImage = captureListener.getImage(CAPTURE_WAIT_TIMEOUT_MS);
/*CameraTestUtils.validateImage(captureImage, s.getWidth(), s.getHeight(),
ImageFormat.RAW_SENSOR, null);*/
- resultImages.add(captureImage);
+ resultImages.add(captureImage);
+ }
+ ret.add(new Pair<List<Image>, CaptureResult>(resultImages, result));
}
// Stop capture, delete the streams.
stopCapture(/*fast*/false);
- return new Pair<List<Image>, CaptureResult>(resultImages, result);
+ return ret;
}
private CaptureRequest.Builder prepareCaptureRequestForSurfaces(List<Surface> surfaces)
@@ -336,7 +584,7 @@
createSession(surfaces);
CaptureRequest.Builder captureBuilder =
- mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+ mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
assertNotNull("Fail to get captureRequest", captureBuilder);
for (Surface surface : surfaces) {
captureBuilder.addTarget(surface);
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
index 9dd5ed8..250601d 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.graphics.ImageFormat;
+import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraCharacteristics.Key;
import android.hardware.camera2.CameraManager;
@@ -25,11 +26,13 @@
import android.hardware.camera2.params.BlackLevelPattern;
import android.hardware.camera2.params.ColorSpaceTransform;
import android.hardware.camera2.params.StreamConfigurationMap;
+import android.media.ImageReader;
import android.test.AndroidTestCase;
import android.util.Log;
import android.util.Rational;
import android.util.Range;
import android.util.Size;
+import android.view.Surface;
import java.util.ArrayList;
import java.util.Arrays;
@@ -437,6 +440,179 @@
}
/**
+ * Cross-check StreamConfigurationMap output
+ */
+ public void testStreamConfigurationMap() {
+ int counter = 0;
+ for (CameraCharacteristics c : mCharacteristics) {
+ Log.i(TAG, "testStreamConfigurationMap: Testing camera ID " + mIds[counter]);
+ StreamConfigurationMap config =
+ c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+ assertNotNull(String.format("No stream configuration map found for: ID %s",
+ mIds[counter]), config);
+
+ assertTrue("ImageReader must be supported",
+ config.isOutputSupportedFor(android.media.ImageReader.class));
+ assertTrue("MediaRecorder must be supported",
+ config.isOutputSupportedFor(android.media.MediaRecorder.class));
+ assertTrue("MediaCodec must be supported",
+ config.isOutputSupportedFor(android.media.MediaCodec.class));
+ assertTrue("Allocation must be supported",
+ config.isOutputSupportedFor(android.renderscript.Allocation.class));
+ assertTrue("SurfaceHolder must be supported",
+ config.isOutputSupportedFor(android.view.SurfaceHolder.class));
+ assertTrue("SurfaceTexture must be supported",
+ config.isOutputSupportedFor(android.graphics.SurfaceTexture.class));
+
+ assertTrue("YUV_420_888 must be supported",
+ config.isOutputSupportedFor(ImageFormat.YUV_420_888));
+ assertTrue("JPEG must be supported",
+ config.isOutputSupportedFor(ImageFormat.JPEG));
+
+ // Legacy YUV formats should not be listed
+ assertTrue("NV21 must not be supported",
+ !config.isOutputSupportedFor(ImageFormat.NV21));
+
+ int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
+ assertNotNull("android.request.availableCapabilities must never be null",
+ actualCapabilities);
+ if (arrayContains(actualCapabilities,
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
+ assertTrue("RAW_SENSOR must be supported if RAW capability is advertised",
+ config.isOutputSupportedFor(ImageFormat.RAW_SENSOR));
+ }
+
+ // Cross check public formats and sizes
+
+ int[] supportedFormats = config.getOutputFormats();
+ for (int format : supportedFormats) {
+ assertTrue("Format " + format + " fails cross check",
+ config.isOutputSupportedFor(format));
+ Size[] supportedSizes = config.getOutputSizes(format);
+ assertTrue("Supported format " + format + " has no sizes listed",
+ supportedSizes.length > 0);
+ for (Size size : supportedSizes) {
+ if (VERBOSE) {
+ Log.v(TAG,
+ String.format("Testing camera %s, format %d, size %s",
+ mIds[counter], format, size.toString()));
+ }
+
+ long stallDuration = config.getOutputStallDuration(format, size);
+ switch(format) {
+ case ImageFormat.YUV_420_888:
+ assertTrue("YUV_420_888 may not have a non-zero stall duration",
+ stallDuration == 0);
+ break;
+ default:
+ assertTrue("Negative stall duration for format " + format,
+ stallDuration >= 0);
+ break;
+ }
+ long minDuration = config.getOutputMinFrameDuration(format, size);
+ if (arrayContains(actualCapabilities,
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
+ assertTrue("MANUAL_SENSOR capability, need positive min frame duration for"
+ + "format " + format,
+ minDuration > 0);
+ } else {
+ assertTrue("Need non-negative min frame duration for format " + format,
+ minDuration >= 0);
+ }
+
+ ImageReader testReader = ImageReader.newInstance(
+ size.getWidth(),
+ size.getHeight(),
+ format,
+ 1);
+ Surface testSurface = testReader.getSurface();
+
+ assertTrue(
+ String.format("isOutputSupportedFor fails for config %s, format %d",
+ size.toString(), format),
+ config.isOutputSupportedFor(testSurface));
+
+ testReader.close();
+
+ } // sizes
+
+ // Try an invalid size in this format, should round
+ Size invalidSize = findInvalidSize(supportedSizes);
+ int MAX_ROUNDING_WIDTH = 1920;
+ if (invalidSize.getWidth() <= MAX_ROUNDING_WIDTH) {
+ ImageReader testReader = ImageReader.newInstance(
+ invalidSize.getWidth(),
+ invalidSize.getHeight(),
+ format,
+ 1);
+ Surface testSurface = testReader.getSurface();
+
+ assertTrue(
+ String.format("isOutputSupportedFor fails for config %s, %d",
+ invalidSize.toString(), format),
+ config.isOutputSupportedFor(testSurface));
+
+ testReader.close();
+ }
+ } // formats
+
+ // Cross-check opaque format and sizes
+
+ SurfaceTexture st = new SurfaceTexture(1);
+ Surface surf = new Surface(st);
+
+ Size[] opaqueSizes = config.getOutputSizes(SurfaceTexture.class);
+ assertTrue("Opaque format has no sizes listed",
+ opaqueSizes.length > 0);
+ for (Size size : opaqueSizes) {
+ long stallDuration = config.getOutputStallDuration(SurfaceTexture.class, size);
+ assertTrue("Opaque output may not have a non-zero stall duration",
+ stallDuration == 0);
+
+ long minDuration = config.getOutputMinFrameDuration(SurfaceTexture.class, size);
+ if (arrayContains(actualCapabilities,
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
+ assertTrue("MANUAL_SENSOR capability, need positive min frame duration for"
+ + "opaque format",
+ minDuration > 0);
+ } else {
+ assertTrue("Need non-negative min frame duration for opaque format ",
+ minDuration >= 0);
+ }
+ st.setDefaultBufferSize(size.getWidth(), size.getHeight());
+
+ assertTrue(
+ String.format("isOutputSupportedFor fails for SurfaceTexture config %s",
+ size.toString()),
+ config.isOutputSupportedFor(surf));
+
+ } // opaque sizes
+
+ // Try invalid opaque size, should get rounded
+ Size invalidSize = findInvalidSize(opaqueSizes);
+ st.setDefaultBufferSize(invalidSize.getWidth(), invalidSize.getHeight());
+ assertTrue(
+ String.format("isOutputSupportedFor fails for SurfaceTexture config %s",
+ invalidSize.toString()),
+ config.isOutputSupportedFor(surf));
+
+ counter++;
+ } // mCharacteristics
+
+ }
+
+ /**
+ * Create an invalid size that's close to one of the good sizes in the list, but not one of them
+ */
+ private Size findInvalidSize(Size[] goodSizes) {
+ Size invalidSize = new Size(goodSizes[0].getWidth() + 1, goodSizes[0].getHeight());
+ while(arrayContains(goodSizes, invalidSize)) {
+ invalidSize = new Size(invalidSize.getWidth() + 1, invalidSize.getHeight());
+ }
+ return invalidSize;
+ }
+
+ /**
* Check key is present in characteristics if the hardware level is at least {@code hwLevel};
* check that the key is present if the actual capabilities are one of {@code capabilities}.
*
@@ -509,6 +685,20 @@
return false;
}
+ private static <T> boolean arrayContains(T[] arr, T needle) {
+ if (arr == null) {
+ return false;
+ }
+
+ for (T elem : arr) {
+ if (elem.equals(needle)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
private static boolean arrayContainsAnyOf(int[] arr, int[] needles) {
for (int needle : needles) {
if (arrayContains(arr, needle)) {
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java
index 9089a8c..a410775 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java
@@ -30,6 +30,7 @@
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.cts.helpers.StaticMetadata;
+import android.hardware.camera2.cts.rs.BitmapUtils;
import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
@@ -364,7 +365,7 @@
yuvPatch.width(), yuvPatch.height(), /*filter*/true);
// Compare two patches using average of per-pixel differences
- double difference = findDifferenceMetric(yuvBmap, jpegBmap);
+ double difference = BitmapUtils.calcDifferenceMetric(yuvBmap, jpegBmap);
Log.i(TAG, "Difference for resolution " + captureSz + " is: " +
difference);
@@ -413,39 +414,6 @@
}
/**
- * Find the difference between two bitmaps using average of per-pixel differences.
- *
- * @param a first {@link Bitmap}.
- * @param b second {@link Bitmap}.
- * @return the difference.
- */
- private static double findDifferenceMetric(Bitmap a, Bitmap b) {
- if (a.getWidth() != b.getWidth() || a.getHeight() != b.getHeight()) {
- throw new IllegalArgumentException("Bitmap dimensions for arguments do not match a=" +
- a.getWidth() + "x" + a.getHeight() + ", b=" + b.getWidth() + "x" +
- b.getHeight());
- }
- // TODO: Optimize this in renderscript to avoid copy.
- int[] aPixels = new int[a.getHeight() * a.getWidth()];
- int[] bPixels = new int[aPixels.length];
- a.getPixels(aPixels, /*offset*/0, /*stride*/a.getWidth(), /*x*/0, /*y*/0, a.getWidth(),
- a.getHeight());
- b.getPixels(bPixels, /*offset*/0, /*stride*/b.getWidth(), /*x*/0, /*y*/0, b.getWidth(),
- b.getHeight());
- double diff = 0;
- for (int i = 0; i < aPixels.length; i++) {
- int aPix = aPixels[i];
- int bPix = bPixels[i];
-
- diff += Math.abs(Color.red(aPix) - Color.red(bPix)); // red
- diff += Math.abs(Color.green(aPix) - Color.green(bPix)); // green
- diff += Math.abs(Color.blue(aPix) - Color.blue(bPix)); // blue
- }
- diff /= (aPixels.length * 3);
- return diff;
- }
-
- /**
* Convert a rectangular patch in a YUV image to an ARGB color array.
*
* @param w width of the patch.
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java
index 397407b..0eb3404 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java
@@ -369,7 +369,8 @@
Log.i(TAG, String.format("Testing Camera %s, config %s",
cameraId, MaxOutputSizes.configToString(config)));
- final int TIMEOUT_FOR_RESULT_MS = 1000;
+ // Timeout is relaxed by 500ms for LEGACY devices to reduce false positive rate in CTS
+ final int TIMEOUT_FOR_RESULT_MS = (mStaticInfo.isHardwareLevelLegacy()) ? 1500 : 1000;
final int MIN_RESULT_COUNT = 3;
ImageDropperListener imageDropperListener = new ImageDropperListener();
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/BitmapUtils.java b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/BitmapUtils.java
new file mode 100644
index 0000000..744d2c7
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/BitmapUtils.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2015 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 android.hardware.camera2.cts.rs;
+
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.RenderScript;
+import android.renderscript.ScriptIntrinsicHistogram;
+
+/**
+ * Utility class providing methods for various pixel-wise ARGB bitmap operations.
+ */
+public class BitmapUtils {
+ private static final String TAG = "BitmapUtils";
+ private static final int COLOR_BIT_DEPTH = 256;
+
+ public static int A = 3;
+ public static int R = 0;
+ public static int G = 1;
+ public static int B = 2;
+ public static int NUM_CHANNELS = 4;
+
+ /**
+ * Return the histograms for each color channel (interleaved).
+ *
+ * @param rs a {@link RenderScript} context to use.
+ * @param bmap a {@link Bitmap} to generate the histograms for.
+ * @return an array containing NUM_CHANNELS * COLOR_BIT_DEPTH histogram bucket values, with
+ * the color channels interleaved.
+ */
+ public static int[] calcHistograms(RenderScript rs, Bitmap bmap) {
+ ScriptIntrinsicHistogram hist = ScriptIntrinsicHistogram.create(rs, Element.U8_4(rs));
+ Allocation sums = Allocation.createSized(rs, Element.I32_4(rs), COLOR_BIT_DEPTH);
+
+ // Setup input allocation (ARGB 8888 bitmap).
+ Allocation input = Allocation.createFromBitmap(rs, bmap);
+
+ hist.setOutput(sums);
+ hist.forEach(input);
+ int[] output = new int[COLOR_BIT_DEPTH * NUM_CHANNELS];
+ sums.copyTo(output);
+ return output;
+ }
+
+ /**
+ * Find the difference between two bitmaps using average of per-pixel differences.
+ *
+ * @param a first {@link android.graphics.Bitmap}.
+ * @param b second {@link android.graphics.Bitmap}.
+ * @return the difference.
+ */
+ public static double calcDifferenceMetric(Bitmap a, Bitmap b) {
+ if (a.getWidth() != b.getWidth() || a.getHeight() != b.getHeight()) {
+ throw new IllegalArgumentException("Bitmap dimensions for arguments do not match a=" +
+ a.getWidth() + "x" + a.getHeight() + ", b=" + b.getWidth() + "x" +
+ b.getHeight());
+ }
+ // TODO: Optimize this in renderscript to avoid copy.
+ int[] aPixels = new int[a.getHeight() * a.getWidth()];
+ int[] bPixels = new int[aPixels.length];
+ a.getPixels(aPixels, /*offset*/0, /*stride*/a.getWidth(), /*x*/0, /*y*/0, a.getWidth(),
+ a.getHeight());
+ b.getPixels(bPixels, /*offset*/0, /*stride*/b.getWidth(), /*x*/0, /*y*/0, b.getWidth(),
+ b.getHeight());
+ double diff = 0;
+ for (int i = 0; i < aPixels.length; i++) {
+ int aPix = aPixels[i];
+ int bPix = bPixels[i];
+
+ diff += Math.abs(Color.red(aPix) - Color.red(bPix)); // red
+ diff += Math.abs(Color.green(aPix) - Color.green(bPix)); // green
+ diff += Math.abs(Color.blue(aPix) - Color.blue(bPix)); // blue
+ }
+ diff /= (aPixels.length * 3);
+ return diff;
+ }
+
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/RawConverter.java b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/RawConverter.java
new file mode 100644
index 0000000..2cd2469
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/RawConverter.java
@@ -0,0 +1,786 @@
+/*
+ * Copyright 2015 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 android.hardware.camera2.cts.rs;
+
+import android.graphics.Bitmap;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.params.ColorSpaceTransform;
+import android.hardware.camera2.params.LensShadingMap;
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.Float3;
+import android.renderscript.Float4;
+import android.renderscript.Int4;
+import android.renderscript.Matrix3f;
+import android.renderscript.RenderScript;
+import android.renderscript.Type;
+
+import android.hardware.camera2.cts.ScriptC_raw_converter;
+import android.util.Log;
+import android.util.Rational;
+import android.util.SparseIntArray;
+
+import java.util.Arrays;
+
+/**
+ * Utility class providing methods for rendering RAW16 images into other colorspaces.
+ */
+public class RawConverter {
+ private static final String TAG = "RawConverter";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ /**
+ * Matrix to convert from CIE XYZ colorspace to sRGB, Bradford-adapted to D65.
+ */
+ private static final float[] sXYZtoRGBBradford = new float[] {
+ 3.1338561f, -1.6168667f, -0.4906146f,
+ -0.9787684f, 1.9161415f, 0.0334540f,
+ 0.0719453f, -0.2289914f, 1.4052427f
+ };
+
+ /**
+ * Matrix to convert from the ProPhoto RGB colorspace to CIE XYZ colorspace.
+ */
+ private static final float[] sProPhotoToXYZ = new float[] {
+ 0.797779f, 0.135213f, 0.031303f,
+ 0.288000f, 0.711900f, 0.000100f,
+ 0.000000f, 0.000000f, 0.825105f
+ };
+
+ /**
+ * Matrix to convert from CIE XYZ colorspace to ProPhoto RGB colorspace.
+ */
+ private static final float[] sXYZtoProPhoto = new float[] {
+ 1.345753f, -0.255603f, -0.051025f,
+ -0.544426f, 1.508096f, 0.020472f,
+ 0.000000f, 0.000000f, 1.211968f
+ };
+
+ /**
+ * Coefficients for a 3rd order polynomial, ordered from highest to lowest power. This
+ * polynomial approximates the default tonemapping curve used for ACR3.
+ */
+ private static final float[] DEFAULT_ACR3_TONEMAP_CURVE_COEFFS = new float[] {
+ 1.041f, -2.973f, 2.932f, 0f
+ };
+
+ /**
+ * The D50 whitepoint coordinates in CIE XYZ colorspace.
+ */
+ private static final float[] D50_XYZ = new float[] { 0.9642f, 1, 0.8249f };
+
+ /**
+ * An array containing the color temperatures for standard reference illuminants.
+ */
+ private static final SparseIntArray sStandardIlluminants = new SparseIntArray();
+ private static final int NO_ILLUMINANT = -1;
+ static {
+ sStandardIlluminants.append(CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_DAYLIGHT, 6504);
+ sStandardIlluminants.append(CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_D65, 6504);
+ sStandardIlluminants.append(CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_D50, 5003);
+ sStandardIlluminants.append(CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_D55, 5503);
+ sStandardIlluminants.append(CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_D75, 7504);
+ sStandardIlluminants.append(CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_STANDARD_A, 2856);
+ sStandardIlluminants.append(CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_STANDARD_B, 4874);
+ sStandardIlluminants.append(CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_STANDARD_C, 6774);
+ sStandardIlluminants.append(
+ CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_DAYLIGHT_FLUORESCENT, 6430);
+ sStandardIlluminants.append(
+ CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_COOL_WHITE_FLUORESCENT, 4230);
+ sStandardIlluminants.append(
+ CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_WHITE_FLUORESCENT, 3450);
+ // TODO: Add the rest of the illuminants included in the LightSource EXIF tag.
+ }
+
+ /**
+ * Convert a RAW16 buffer into an sRGB buffer, and write the result into a bitmap.
+ *
+ * <p> This function applies the operations roughly outlined in the Adobe DNG specification
+ * using the provided metadata about the image sensor. Sensor data for Android devices is
+ * assumed to be relatively linear, and no extra linearization step is applied here. The
+ * following operations are applied in the given order:</p>
+ *
+ * <ul>
+ * <li>
+ * Black level subtraction - the black levels given in the SENSOR_BLACK_LEVEL_PATTERN
+ * tag are subtracted from the corresponding raw pixels.
+ * </li>
+ * <li>
+ * Rescaling - each raw pixel is scaled by 1/(white level - black level).
+ * </li>
+ * <li>
+ * Lens shading correction - the interpolated gains from the gain map defined in the
+ * STATISTICS_LENS_SHADING_CORRECTION_MAP are applied to each raw pixel.
+ * </li>
+ * <li>
+ * Clipping - each raw pixel is clipped to a range of [0.0, 1.0].
+ * </li>
+ * <li>
+ * Demosaic - the RGB channels for each pixel are retrieved from the Bayer mosaic
+ * of raw pixels using a simple bilinear-interpolation demosaicing algorithm.
+ * </li>
+ * <li>
+ * Colorspace transform to wide-gamut RGB - each pixel is mapped into a
+ * wide-gamut colorspace (in this case ProPhoto RGB is used) from the sensor
+ * colorspace.
+ * </li>
+ * <li>
+ * Tonemapping - A basic tonemapping curve using the default from ACR3 is applied
+ * (no further exposure compensation is applied here, though this could be improved).
+ * </li>
+ * <li>
+ * Colorspace transform to final RGB - each pixel is mapped into linear sRGB colorspace.
+ * </li>
+ * <li>
+ * Gamma correction - each pixel is gamma corrected using γ=2.2 to map into sRGB
+ * colorspace for viewing.
+ * </li>
+ * <li>
+ * Packing - each pixel is scaled so that each color channel has a range of [0, 255],
+ * and is packed into an Android bitmap.
+ * </li>
+ * </ul>
+ *
+ * <p> Arguments given here are assumed to come from the values for the corresponding
+ * {@link CameraCharacteristics.Key}s defined for the camera that produced this RAW16 buffer.
+ * </p>
+ * @param rs a {@link RenderScript} context to use.
+ * @param inputWidth width of the input RAW16 image in pixels.
+ * @param inputHeight height of the input RAW16 image in pixels.
+ * @param rawImageInput a byte array containing a RAW16 image.
+ * @param staticMetadata the {@link CameraCharacteristics} for this RAW capture.
+ * @param dynamicMetadata the {@link CaptureResult} for this RAW capture.
+ * @param outputOffsetX the offset width into the raw image of the left side of the output
+ * rectangle.
+ * @param outputOffsetY the offset height into the raw image of the top side of the output
+ * rectangle.
+ * @param argbOutput a {@link Bitmap} to output the rendered RAW image into. The height and
+ * width of this bitmap along with the output offsets are used to determine
+ * the dimensions and offset of the output rectangle contained in the RAW
+ * image to be rendered.
+ */
+ public static void convertToSRGB(RenderScript rs, int inputWidth, int inputHeight,
+ byte[] rawImageInput, CameraCharacteristics staticMetadata,
+ CaptureResult dynamicMetadata, int outputOffsetX, int outputOffsetY,
+ /*out*/Bitmap argbOutput) {
+ int cfa = staticMetadata.get(CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
+ int[] blackLevelPattern = new int[4];
+ staticMetadata.get(CameraCharacteristics.SENSOR_BLACK_LEVEL_PATTERN).
+ copyTo(blackLevelPattern, /*offset*/0);
+ int whiteLevel = staticMetadata.get(CameraCharacteristics.SENSOR_INFO_WHITE_LEVEL);
+ int ref1 = staticMetadata.get(CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1);
+ int ref2 = staticMetadata.get(CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2);
+ float[] calib1 = new float[9];
+ float[] calib2 = new float[9];
+ convertColorspaceTransform(
+ staticMetadata.get(CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM1), calib1);
+ convertColorspaceTransform(
+ staticMetadata.get(CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM2), calib2);
+ float[] color1 = new float[9];
+ float[] color2 = new float[9];
+ convertColorspaceTransform(
+ staticMetadata.get(CameraCharacteristics.SENSOR_COLOR_TRANSFORM1), color1);
+ convertColorspaceTransform(
+ staticMetadata.get(CameraCharacteristics.SENSOR_COLOR_TRANSFORM2), color2);
+ float[] forward1 = new float[9];
+ float[] forward2 = new float[9];
+ convertColorspaceTransform(
+ staticMetadata.get(CameraCharacteristics.SENSOR_FORWARD_MATRIX1), forward1);
+ convertColorspaceTransform(
+ staticMetadata.get(CameraCharacteristics.SENSOR_FORWARD_MATRIX2), forward2);
+
+ Rational[] neutral = dynamicMetadata.get(CaptureResult.SENSOR_NEUTRAL_COLOR_POINT);
+
+ LensShadingMap shadingMap = dynamicMetadata.get(CaptureResult.STATISTICS_LENS_SHADING_CORRECTION_MAP);
+
+ convertToSRGB(rs, inputWidth, inputHeight, cfa, blackLevelPattern, whiteLevel,
+ rawImageInput, ref1, ref2, calib1, calib2, color1, color2,
+ forward1, forward2, neutral, shadingMap, outputOffsetX, outputOffsetY, argbOutput);
+ }
+
+ /**
+ * Convert a RAW16 buffer into an sRGB buffer, and write the result into a bitmap.
+ *
+ * @see #convertToSRGB
+ */
+ private static void convertToSRGB(RenderScript rs, int inputWidth, int inputHeight, int cfa,
+ int[] blackLevelPattern, int whiteLevel, byte[] rawImageInput,
+ int referenceIlluminant1, int referenceIlluminant2, float[] calibrationTransform1,
+ float[] calibrationTransform2, float[] colorMatrix1, float[] colorMatrix2,
+ float[] forwardTransform1, float[] forwardTransform2, Rational[/*3*/] neutralColorPoint,
+ LensShadingMap lensShadingMap, int outputOffsetX, int outputOffsetY,
+ /*out*/Bitmap argbOutput) {
+
+ // Validate arguments
+ if (argbOutput == null || rs == null || rawImageInput == null) {
+ throw new IllegalArgumentException("Null argument to convertToSRGB");
+ }
+ if (argbOutput.getConfig() != Bitmap.Config.ARGB_8888) {
+ throw new IllegalArgumentException(
+ "Output bitmap passed to convertToSRGB is not ARGB_8888 format");
+ }
+ if (outputOffsetX < 0 || outputOffsetY < 0) {
+ throw new IllegalArgumentException("Negative offset passed to convertToSRGB");
+ }
+ int outWidth = argbOutput.getWidth();
+ int outHeight = argbOutput.getHeight();
+ if (outWidth + outputOffsetX > inputWidth || outHeight + outputOffsetY > inputHeight) {
+ throw new IllegalArgumentException("Raw image with dimensions (w=" + inputWidth +
+ ", h=" + inputHeight + "), cannot converted into sRGB image with dimensions (w="
+ + outWidth + ", h=" + outHeight + ").");
+ }
+ if (cfa < 0 || cfa > 3) {
+ throw new IllegalArgumentException("Unsupported cfa pattern " + cfa + " used.");
+ }
+ if (DEBUG) {
+ Log.d(TAG, "Metadata Used:");
+ Log.d(TAG, "Input width,height: " + inputWidth + "," + inputHeight);
+ Log.d(TAG, "Output offset x,y: " + outputOffsetX + "," + outputOffsetY);
+ Log.d(TAG, "Output width,height: " + outWidth + "," + outHeight);
+ Log.d(TAG, "CFA: " + cfa);
+ Log.d(TAG, "BlackLevelPattern: " + Arrays.toString(blackLevelPattern));
+ Log.d(TAG, "WhiteLevel: " + whiteLevel);
+ Log.d(TAG, "ReferenceIlluminant1: " + referenceIlluminant1);
+ Log.d(TAG, "ReferenceIlluminant2: " + referenceIlluminant2);
+ Log.d(TAG, "CalibrationTransform1: " + Arrays.toString(calibrationTransform1));
+ Log.d(TAG, "CalibrationTransform2: " + Arrays.toString(calibrationTransform2));
+ Log.d(TAG, "ColorMatrix1: " + Arrays.toString(colorMatrix1));
+ Log.d(TAG, "ColorMatrix2: " + Arrays.toString(colorMatrix2));
+ Log.d(TAG, "ForwardTransform1: " + Arrays.toString(forwardTransform1));
+ Log.d(TAG, "ForwardTransform2: " + Arrays.toString(forwardTransform2));
+ Log.d(TAG, "NeutralColorPoint: " + Arrays.toString(neutralColorPoint));
+ }
+
+ Allocation gainMap = null;
+ if (lensShadingMap != null) {
+ float[] lsm = new float[lensShadingMap.getGainFactorCount()];
+ lensShadingMap.copyGainFactors(/*inout*/lsm, /*offset*/0);
+ gainMap = createFloat4Allocation(rs, lsm, lensShadingMap.getColumnCount(),
+ lensShadingMap.getRowCount());
+ }
+
+ float[] normalizedForwardTransform1 = Arrays.copyOf(forwardTransform1,
+ forwardTransform1.length);
+ normalizeFM(normalizedForwardTransform1);
+ float[] normalizedForwardTransform2 = Arrays.copyOf(forwardTransform2,
+ forwardTransform2.length);
+ normalizeFM(normalizedForwardTransform2);
+
+ float[] normalizedColorMatrix1 = Arrays.copyOf(colorMatrix1, colorMatrix1.length);
+ normalizeCM(normalizedColorMatrix1);
+ float[] normalizedColorMatrix2 = Arrays.copyOf(colorMatrix2, colorMatrix2.length);
+ normalizeCM(normalizedColorMatrix2);
+
+ if (DEBUG) {
+ Log.d(TAG, "Normalized ForwardTransform1: " + Arrays.toString(normalizedForwardTransform1));
+ Log.d(TAG, "Normalized ForwardTransform2: " + Arrays.toString(normalizedForwardTransform2));
+ Log.d(TAG, "Normalized ColorMatrix1: " + Arrays.toString(normalizedColorMatrix1));
+ Log.d(TAG, "Normalized ColorMatrix2: " + Arrays.toString(normalizedColorMatrix2));
+ }
+
+ // Calculate full sensor colorspace to sRGB colorspace transform.
+ double interpolationFactor = findDngInterpolationFactor(referenceIlluminant1,
+ referenceIlluminant2, calibrationTransform1, calibrationTransform2,
+ normalizedColorMatrix1, normalizedColorMatrix2, neutralColorPoint);
+ if (DEBUG) Log.d(TAG, "Interpolation factor used: " + interpolationFactor);
+ float[] sensorToXYZ = new float[9];
+ calculateCameraToXYZD50Transform(normalizedForwardTransform1, normalizedForwardTransform2,
+ calibrationTransform1, calibrationTransform2, neutralColorPoint,
+ interpolationFactor, /*out*/sensorToXYZ);
+ if (DEBUG) Log.d(TAG, "CameraToXYZ xform used: " + Arrays.toString(sensorToXYZ));
+ float[] sensorToProPhoto = new float[9];
+ multiply(sXYZtoProPhoto, sensorToXYZ, /*out*/sensorToProPhoto);
+ if (DEBUG) Log.d(TAG, "CameraToIntemediate xform used: " + Arrays.toString(sensorToProPhoto));
+ Allocation output = Allocation.createFromBitmap(rs, argbOutput);
+
+ float[] proPhotoToSRGB = new float[9];
+ multiply(sXYZtoRGBBradford, sProPhotoToXYZ, /*out*/proPhotoToSRGB);
+
+ // Setup input allocation (16-bit raw pixels)
+ Type.Builder typeBuilder = new Type.Builder(rs, Element.U16(rs));
+ typeBuilder.setX(inputWidth);
+ typeBuilder.setY(inputHeight);
+ Type inputType = typeBuilder.create();
+ Allocation input = Allocation.createTyped(rs, inputType);
+ input.copyFromUnchecked(rawImageInput);
+
+ // Setup RS kernel globals
+ ScriptC_raw_converter converterKernel = new ScriptC_raw_converter(rs);
+ converterKernel.set_inputRawBuffer(input);
+ converterKernel.set_whiteLevel(whiteLevel);
+ converterKernel.set_sensorToIntermediate(new Matrix3f(transpose(sensorToProPhoto)));
+ converterKernel.set_intermediateToSRGB(new Matrix3f(transpose(proPhotoToSRGB)));
+ converterKernel.set_offsetX(outputOffsetX);
+ converterKernel.set_offsetY(outputOffsetY);
+ converterKernel.set_rawHeight(inputHeight);
+ converterKernel.set_rawWidth(inputWidth);
+ converterKernel.set_neutralPoint(new Float3(neutralColorPoint[0].floatValue(),
+ neutralColorPoint[1].floatValue(), neutralColorPoint[2].floatValue()));
+ converterKernel.set_toneMapCoeffs(new Float4(DEFAULT_ACR3_TONEMAP_CURVE_COEFFS[0],
+ DEFAULT_ACR3_TONEMAP_CURVE_COEFFS[1], DEFAULT_ACR3_TONEMAP_CURVE_COEFFS[2],
+ DEFAULT_ACR3_TONEMAP_CURVE_COEFFS[3]));
+ converterKernel.set_hasGainMap(gainMap != null);
+ if (gainMap != null) {
+ converterKernel.set_gainMap(gainMap);
+ converterKernel.set_gainMapWidth(lensShadingMap.getColumnCount());
+ converterKernel.set_gainMapHeight(lensShadingMap.getRowCount());
+ }
+
+ converterKernel.set_cfaPattern(cfa);
+ converterKernel.set_blackLevelPattern(new Int4(blackLevelPattern[0],
+ blackLevelPattern[1], blackLevelPattern[2], blackLevelPattern[3]));
+ converterKernel.forEach_convert_RAW_To_ARGB(output);
+ output.copyTo(argbOutput); // Force RS sync with bitmap (does not do an extra copy).
+ }
+
+ /**
+ * Create a float-backed renderscript {@link Allocation} with the given dimensions, containing
+ * the contents of the given float array.
+ *
+ * @param rs a {@link RenderScript} context to use.
+ * @param fArray the float array to copy into the {@link Allocation}.
+ * @param width the width of the {@link Allocation}.
+ * @param height the height of the {@link Allocation}.
+ * @return an {@link Allocation} containing the given floats.
+ */
+ private static Allocation createFloat4Allocation(RenderScript rs, float[] fArray,
+ int width, int height) {
+ if (fArray.length != width * height * 4) {
+ throw new IllegalArgumentException("Invalid float array of length " + fArray.length +
+ ", must be correct size for Allocation of dimensions " + width + "x" + height);
+ }
+ Type.Builder builder = new Type.Builder(rs, Element.F32_4(rs));
+ builder.setX(width);
+ builder.setY(height);
+ Allocation fAlloc = Allocation.createTyped(rs, builder.create());
+ fAlloc.copyFrom(fArray);
+ return fAlloc;
+ }
+
+ /**
+ * Calculate the correlated color temperature (CCT) for a given x,y chromaticity in CIE 1931 x,y
+ * chromaticity space using McCamy's cubic approximation algorithm given in:
+ *
+ * McCamy, Calvin S. (April 1992).
+ * "Correlated color temperature as an explicit function of chromaticity coordinates".
+ * Color Research & Application 17 (2): 142–144
+ *
+ * @param x x chromaticity component.
+ * @param y y chromaticity component.
+ *
+ * @return the CCT associated with this chromaticity coordinate.
+ */
+ private static double calculateColorTemperature(double x, double y) {
+ double n = (x - 0.332) / (y - 0.1858);
+ return -449 * Math.pow(n, 3) + 3525 * Math.pow(n, 2) - 6823.3 * n + 5520.33;
+ }
+
+ /**
+ * Calculate the x,y chromaticity coordinates in CIE 1931 x,y chromaticity space from the given
+ * CIE XYZ coordinates.
+ *
+ * @param X the CIE XYZ X coordinate.
+ * @param Y the CIE XYZ Y coordinate.
+ * @param Z the CIE XYZ Z coordinate.
+ *
+ * @return the [x, y] chromaticity coordinates as doubles.
+ */
+ private static double[] calculateCIExyCoordinates(double X, double Y, double Z) {
+ double[] ret = new double[] { 0, 0 };
+ ret[0] = X / (X + Y + Z);
+ ret[1] = Y / (X + Y + Z);
+ return ret;
+ }
+
+ /**
+ * Linearly interpolate between a and b given fraction f.
+ *
+ * @param a first term to interpolate between, a will be returned when f == 0.
+ * @param b second term to interpolate between, b will be returned when f == 1.
+ * @param f the fraction to interpolate by.
+ *
+ * @return interpolated result as double.
+ */
+ private static double lerp(double a, double b, double f) {
+ return (a * (1.0f - f)) + (b * f);
+ }
+
+ /**
+ * Linearly interpolate between 3x3 matrices a and b given fraction f.
+ *
+ * @param a first 3x3 matrix to interpolate between, a will be returned when f == 0.
+ * @param b second 3x3 matrix to interpolate between, b will be returned when f == 1.
+ * @param f the fraction to interpolate by.
+ * @param result will be set to contain the interpolated matrix.
+ */
+ private static void lerp(float[] a, float[] b, double f, /*out*/float[] result) {
+ for (int i = 0; i < 9; i++) {
+ result[i] = (float) lerp(a[i], b[i], f);
+ }
+ }
+
+ /**
+ * Convert a 9x9 {@link ColorSpaceTransform} to a matrix and write the matrix into the
+ * output.
+ *
+ * @param xform a {@link ColorSpaceTransform} to transform.
+ * @param output the 3x3 matrix to overwrite.
+ */
+ private static void convertColorspaceTransform(ColorSpaceTransform xform, /*out*/float[] output) {
+ for (int i = 0; i < 3; i++) {
+ for (int j = 0; j < 3; j++) {
+ output[i * 3 + j] = xform.getElement(j, i).floatValue();
+ }
+ }
+ }
+
+ /**
+ * Find the interpolation factor to use with the RAW matrices given a neutral color point.
+ *
+ * @param referenceIlluminant1 first reference illuminant.
+ * @param referenceIlluminant2 second reference illuminant.
+ * @param calibrationTransform1 calibration matrix corresponding to the first reference
+ * illuminant.
+ * @param calibrationTransform2 calibration matrix corresponding to the second reference
+ * illuminant.
+ * @param colorMatrix1 color matrix corresponding to the first reference illuminant.
+ * @param colorMatrix2 color matrix corresponding to the second reference illuminant.
+ * @param neutralColorPoint the neutral color point used to calculate the interpolation factor.
+ *
+ * @return the interpolation factor corresponding to the given neutral color point.
+ */
+ private static double findDngInterpolationFactor(int referenceIlluminant1,
+ int referenceIlluminant2, float[] calibrationTransform1, float[] calibrationTransform2,
+ float[] colorMatrix1, float[] colorMatrix2, Rational[/*3*/] neutralColorPoint) {
+
+ int colorTemperature1 = sStandardIlluminants.get(referenceIlluminant1, NO_ILLUMINANT);
+ if (colorTemperature1 == NO_ILLUMINANT) {
+ throw new IllegalArgumentException("No such illuminant for reference illuminant 1: " +
+ referenceIlluminant1);
+ }
+
+ int colorTemperature2 = sStandardIlluminants.get(referenceIlluminant2, NO_ILLUMINANT);
+ if (colorTemperature2 == NO_ILLUMINANT) {
+ throw new IllegalArgumentException("No such illuminant for reference illuminant 2: " +
+ referenceIlluminant2);
+ }
+
+ if (DEBUG) Log.d(TAG, "ColorTemperature1: " + colorTemperature1);
+ if (DEBUG) Log.d(TAG, "ColorTemperature2: " + colorTemperature2);
+
+ double interpFactor = 0.5; // Initial guess for interpolation factor
+ double oldInterpFactor = interpFactor;
+
+ double lastDiff = Double.MAX_VALUE;
+ double tolerance = 0.0001;
+ float[] XYZToCamera1 = new float[9];
+ float[] XYZToCamera2 = new float[9];
+ multiply(calibrationTransform1, colorMatrix1, /*out*/XYZToCamera1);
+ multiply(calibrationTransform2, colorMatrix2, /*out*/XYZToCamera2);
+
+ float[] cameraNeutral = new float[] { neutralColorPoint[0].floatValue(),
+ neutralColorPoint[1].floatValue(), neutralColorPoint[2].floatValue()};
+
+ float[] neutralGuess = new float[3];
+ float[] interpXYZToCamera = new float[9];
+ float[] interpXYZToCameraInverse = new float[9];
+
+
+ double lower = Math.min(colorTemperature1, colorTemperature2);
+ double upper = Math.max(colorTemperature1, colorTemperature2);
+
+ if(DEBUG) {
+ Log.d(TAG, "XYZtoCamera1: " + Arrays.toString(XYZToCamera1));
+ Log.d(TAG, "XYZtoCamera2: " + Arrays.toString(XYZToCamera2));
+ Log.d(TAG, "Finding interpolation factor, initial guess 0.5...");
+ }
+ // Iteratively guess xy value, find new CCT, and update interpolation factor.
+ int loopLimit = 30;
+ int count = 0;
+ while (lastDiff > tolerance && loopLimit > 0) {
+ if (DEBUG) Log.d(TAG, "Loop count " + count);
+ lerp(XYZToCamera1, XYZToCamera2, interpFactor, interpXYZToCamera);
+ if (!invert(interpXYZToCamera, /*out*/interpXYZToCameraInverse)) {
+ throw new IllegalArgumentException(
+ "Cannot invert XYZ to Camera matrix, input matrices are invalid.");
+ }
+
+ map(interpXYZToCameraInverse, cameraNeutral, /*out*/neutralGuess);
+ double[] xy = calculateCIExyCoordinates(neutralGuess[0], neutralGuess[1],
+ neutralGuess[2]);
+
+ double colorTemperature = calculateColorTemperature(xy[0], xy[1]);
+
+ if (colorTemperature <= lower) {
+ interpFactor = 1;
+ } else if (colorTemperature >= upper) {
+ interpFactor = 0;
+ } else {
+ double invCT = 1.0 / colorTemperature;
+ interpFactor = (invCT - 1.0 / upper) / ( 1.0 / lower - 1.0 / upper);
+ }
+
+ if (lower == colorTemperature1) {
+ interpFactor = 1.0 - interpFactor;
+ }
+
+ interpFactor = (interpFactor + oldInterpFactor) / 2;
+ lastDiff = Math.abs(oldInterpFactor - interpFactor);
+ oldInterpFactor = interpFactor;
+ loopLimit--;
+ count++;
+
+ if (DEBUG) {
+ Log.d(TAG, "CameraToXYZ chosen: " + Arrays.toString(interpXYZToCameraInverse));
+ Log.d(TAG, "XYZ neutral color guess: " + Arrays.toString(neutralGuess));
+ Log.d(TAG, "xy coordinate: " + Arrays.toString(xy));
+ Log.d(TAG, "xy color temperature: " + colorTemperature);
+ Log.d(TAG, "New interpolation factor: " + interpFactor);
+ }
+ }
+
+ if (loopLimit == 0) {
+ Log.w(TAG, "Could not converge on interpolation factor, using factor " + interpFactor +
+ " with remaining error factor of " + lastDiff);
+ }
+ return interpFactor;
+ }
+
+ /**
+ * Calculate the transform from the raw camera sensor colorspace to CIE XYZ colorspace with a
+ * D50 whitepoint.
+ *
+ * @param forwardTransform1 forward transform matrix corresponding to the first reference
+ * illuminant.
+ * @param forwardTransform2 forward transform matrix corresponding to the second reference
+ * illuminant.
+ * @param calibrationTransform1 calibration transform matrix corresponding to the first
+ * reference illuminant.
+ * @param calibrationTransform2 calibration transform matrix corresponding to the second
+ * reference illuminant.
+ * @param neutralColorPoint the neutral color point used to calculate the interpolation factor.
+ * @param interpolationFactor the interpolation factor to use for the forward and
+ * calibration transforms.
+ * @param outputTransform set to the full sensor to XYZ colorspace transform.
+ */
+ private static void calculateCameraToXYZD50Transform(float[] forwardTransform1,
+ float[] forwardTransform2, float[] calibrationTransform1, float[] calibrationTransform2,
+ Rational[/*3*/] neutralColorPoint, double interpolationFactor,
+ /*out*/float[] outputTransform) {
+ float[] cameraNeutral = new float[] { neutralColorPoint[0].floatValue(),
+ neutralColorPoint[1].floatValue(), neutralColorPoint[2].floatValue()};
+ if (DEBUG) Log.d(TAG, "Camera neutral: " + Arrays.toString(cameraNeutral));
+
+ float[] interpolatedCC = new float[9];
+ lerp(calibrationTransform1, calibrationTransform2, interpolationFactor,
+ interpolatedCC);
+ float[] inverseInterpolatedCC = new float[9];
+ if (!invert(interpolatedCC, /*out*/inverseInterpolatedCC)) {
+ throw new IllegalArgumentException( "Cannot invert interpolated calibration transform" +
+ ", input matrices are invalid.");
+ }
+ if (DEBUG) Log.d(TAG, "Inverted interpolated CalibrationTransform: " +
+ Arrays.toString(inverseInterpolatedCC));
+
+ float[] referenceNeutral = new float[3];
+ map(inverseInterpolatedCC, cameraNeutral, /*out*/referenceNeutral);
+ if (DEBUG) Log.d(TAG, "Reference neutral: " + Arrays.toString(referenceNeutral));
+ float[] D = new float[] { 1/referenceNeutral[0], 0, 0, 0, 1/referenceNeutral[1], 0, 0, 0,
+ 1/referenceNeutral[2] };
+ if (DEBUG) Log.d(TAG, "Reference Neutral Diagonal: " + Arrays.toString(D));
+
+ float[] intermediate = new float[9];
+ float[] intermediate2 = new float[9];
+
+ lerp(forwardTransform1, forwardTransform2, interpolationFactor, /*out*/intermediate);
+ if (DEBUG) Log.d(TAG, "Interpolated ForwardTransform: " + Arrays.toString(intermediate));
+
+ multiply(D, inverseInterpolatedCC, /*out*/intermediate2);
+ multiply(intermediate, intermediate2, /*out*/outputTransform);
+ }
+
+ /**
+ * Map a 3d column vector using the given matrix.
+ *
+ * @param matrix float array containing 3x3 matrix to map vector by.
+ * @param input 3 dimensional vector to map.
+ * @param output 3 dimensional vector result.
+ */
+ private static void map(float[] matrix, float[] input, /*out*/float[] output) {
+ output[0] = input[0] * matrix[0] + input[1] * matrix[1] + input[2] * matrix[2];
+ output[1] = input[0] * matrix[3] + input[1] * matrix[4] + input[2] * matrix[5];
+ output[2] = input[0] * matrix[6] + input[1] * matrix[7] + input[2] * matrix[8];
+ }
+
+ /**
+ * Multiply two 3x3 matrices together: A * B
+ *
+ * @param a left matrix.
+ * @param b right matrix.
+ */
+ private static void multiply(float[] a, float[] b, /*out*/float[] output) {
+ output[0] = a[0] * b[0] + a[1] * b[3] + a[2] * b[6];
+ output[3] = a[3] * b[0] + a[4] * b[3] + a[5] * b[6];
+ output[6] = a[6] * b[0] + a[7] * b[3] + a[8] * b[6];
+ output[1] = a[0] * b[1] + a[1] * b[4] + a[2] * b[7];
+ output[4] = a[3] * b[1] + a[4] * b[4] + a[5] * b[7];
+ output[7] = a[6] * b[1] + a[7] * b[4] + a[8] * b[7];
+ output[2] = a[0] * b[2] + a[1] * b[5] + a[2] * b[8];
+ output[5] = a[3] * b[2] + a[4] * b[5] + a[5] * b[8];
+ output[8] = a[6] * b[2] + a[7] * b[5] + a[8] * b[8];
+ }
+
+ /**
+ * Transpose a 3x3 matrix in-place.
+ *
+ * @param m the matrix to transpose.
+ * @return the transposed matrix.
+ */
+ private static float[] transpose(/*inout*/float[/*9*/] m) {
+ float t = m[1];
+ m[1] = m[3];
+ m[3] = t;
+ t = m[2];
+ m[2] = m[6];
+ m[6] = t;
+ t = m[5];
+ m[5] = m[7];
+ m[7] = t;
+ return m;
+ }
+
+ /**
+ * Invert a 3x3 matrix, or return false if the matrix is singular.
+ *
+ * @param m matrix to invert.
+ * @param output set the output to be the inverse of m.
+ */
+ private static boolean invert(float[] m, /*out*/float[] output) {
+ double a00 = m[0];
+ double a01 = m[1];
+ double a02 = m[2];
+ double a10 = m[3];
+ double a11 = m[4];
+ double a12 = m[5];
+ double a20 = m[6];
+ double a21 = m[7];
+ double a22 = m[8];
+
+ double t00 = a11 * a22 - a21 * a12;
+ double t01 = a21 * a02 - a01 * a22;
+ double t02 = a01 * a12 - a11 * a02;
+ double t10 = a20 * a12 - a10 * a22;
+ double t11 = a00 * a22 - a20 * a02;
+ double t12 = a10 * a02 - a00 * a12;
+ double t20 = a10 * a21 - a20 * a11;
+ double t21 = a20 * a01 - a00 * a21;
+ double t22 = a00 * a11 - a10 * a01;
+
+ double det = a00 * t00 + a01 * t10 + a02 * t20;
+ if (Math.abs(det) < 1e-9) {
+ return false; // Inverse too close to zero, not invertible.
+ }
+
+ output[0] = (float) (t00 / det);
+ output[1] = (float) (t01 / det);
+ output[2] = (float) (t02 / det);
+ output[3] = (float) (t10 / det);
+ output[4] = (float) (t11 / det);
+ output[5] = (float) (t12 / det);
+ output[6] = (float) (t20 / det);
+ output[7] = (float) (t21 / det);
+ output[8] = (float) (t22 / det);
+ return true;
+ }
+
+ /**
+ * Scale each element in a matrix by the given scaling factor.
+ *
+ * @param factor factor to scale by.
+ * @param matrix the float array containing a 3x3 matrix to scale.
+ */
+ private static void scale(float factor, /*inout*/float[] matrix) {
+ for (int i = 0; i < 9; i++) {
+ matrix[i] *= factor;
+ }
+ }
+
+ /**
+ * Clamp a value to a given range.
+ *
+ * @param low lower bound to clamp to.
+ * @param high higher bound to clamp to.
+ * @param value the value to clamp.
+ * @return the clamped value.
+ */
+ private static double clamp(double low, double high, double value) {
+ return Math.max(low, Math.min(high, value));
+ }
+
+ /**
+ * Return the max float in the array.
+ *
+ * @param array array of floats to search.
+ * @return max float in the array.
+ */
+ private static float max(float[] array) {
+ float val = array[0];
+ for (float f : array) {
+ val = (f > val) ? f : val;
+ }
+ return val;
+ }
+
+ /**
+ * Normalize ColorMatrix to eliminate headroom for input space scaled to [0, 1] using
+ * the D50 whitepoint. This maps the D50 whitepoint into the colorspace used by the
+ * ColorMatrix, then uses the resulting whitepoint to renormalize the ColorMatrix so
+ * that the channel values in the resulting whitepoint for this operation are clamped
+ * to the range [0, 1].
+ *
+ * @param colorMatrix a 3x3 matrix containing a DNG ColorMatrix to be normalized.
+ */
+ private static void normalizeCM(/*inout*/float[] colorMatrix) {
+ float[] tmp = new float[3];
+ map(colorMatrix, D50_XYZ, /*out*/tmp);
+ float maxVal = max(tmp);
+ if (maxVal > 0) {
+ scale(1.0f / maxVal, colorMatrix);
+ }
+ }
+
+ /**
+ * Normalize ForwardMatrix to ensure that sensor whitepoint [1, 1, 1] maps to D50 in CIE XYZ
+ * colorspace.
+ *
+ * @param forwardMatrix a 3x3 matrix containing a DNG ForwardTransform to be normalized.
+ */
+ private static void normalizeFM(/*inout*/float[] forwardMatrix) {
+ float[] tmp = new float[] {1, 1, 1};
+ float[] xyz = new float[3];
+ map(forwardMatrix, tmp, /*out*/xyz);
+
+ float[] intermediate = new float[9];
+ float[] m = new float[] {1.0f / xyz[0], 0, 0, 0, 1.0f / xyz[1], 0, 0, 0, 1.0f / xyz[2]};
+
+ multiply(m, forwardMatrix, /*out*/ intermediate);
+ float[] m2 = new float[] {D50_XYZ[0], 0, 0, 0, D50_XYZ[1], 0, 0, 0, D50_XYZ[2]};
+ multiply(m2, intermediate, /*out*/forwardMatrix);
+ }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/raw_converter.rs b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/raw_converter.rs
new file mode 100644
index 0000000..c8b353e
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/raw_converter.rs
@@ -0,0 +1,369 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#include "../common.rs"
+
+// This file includes a conversion kernel for RGGB, GRBG, GBRG, and BGGR Bayer patterns.
+// Applying this script also will apply black-level subtraction, rescaling, clipping, tonemapping,
+// and color space transforms along with the Bayer demosaic. See RawConverter.java
+// for more information.
+
+// Input globals
+
+rs_allocation inputRawBuffer; // RAW16 buffer of dimensions (raw image stride) * (raw image height)
+rs_allocation gainMap; // Gainmap to apply to linearized raw sensor data.
+uint cfaPattern; // The Color Filter Arrangement pattern used
+uint gainMapWidth; // The width of the gain map
+uint gainMapHeight; // The height of the gain map
+bool hasGainMap; // Does gainmap exist?
+rs_matrix3x3 sensorToIntermediate; // Color transform from sensor to a wide-gamut colorspace
+rs_matrix3x3 intermediateToSRGB; // Color transform from wide-gamut colorspace to sRGB
+ushort4 blackLevelPattern; // Blacklevel to subtract for each channel, given in CFA order
+int whiteLevel; // Whitelevel of sensor
+uint offsetX; // X offset into inputRawBuffer
+uint offsetY; // Y offset into inputRawBuffer
+uint rawWidth; // Width of raw buffer
+uint rawHeight; // Height of raw buffer
+float3 neutralPoint; // The camera neutral
+float4 toneMapCoeffs; // Coefficients for a polynomial tonemapping curve
+
+// Interpolate gain map to find per-channel gains at a given pixel
+static float4 getGain(uint x, uint y) {
+ float interpX = (((float) x) / rawWidth) * gainMapWidth;
+ float interpY = (((float) y) / rawHeight) * gainMapHeight;
+ uint gX = (uint) interpX;
+ uint gY = (uint) interpY;
+ uint gXNext = (gX + 1 < gainMapWidth) ? gX + 1 : gX;
+ uint gYNext = (gY + 1 < gainMapHeight) ? gY + 1 : gY;
+
+ float4 tl = *((float4 *) rsGetElementAt(gainMap, gX, gY));
+ float4 tr = *((float4 *) rsGetElementAt(gainMap, gXNext, gY));
+ float4 bl = *((float4 *) rsGetElementAt(gainMap, gX, gYNext));
+ float4 br = *((float4 *) rsGetElementAt(gainMap, gXNext, gYNext));
+
+ float fracX = interpX - (float) gX;
+ float fracY = interpY - (float) gY;
+ float invFracX = 1.f - fracX;
+ float invFracY = 1.f - fracY;
+
+ return tl * invFracX * invFracY + tr * fracX * invFracY +
+ bl * invFracX * fracY + br * fracX * fracY;
+}
+
+// Apply gamma correction using sRGB gamma curve
+static float gammaEncode(float x) {
+ return (x <= 0.0031308f) ? x * 12.92f : 1.055f * pow(x, 0.4166667f) - 0.055f;
+}
+
+// Apply gamma correction to each color channel in RGB pixel
+static float3 gammaCorrectPixel(float3 rgb) {
+ float3 ret;
+ ret.x = gammaEncode(rgb.x);
+ ret.y = gammaEncode(rgb.y);
+ ret.z = gammaEncode(rgb.z);
+ return ret;
+}
+
+// Apply polynomial tonemapping curve to each color channel in RGB pixel.
+// This attempts to apply tonemapping without changing the hue of each pixel,
+// i.e.:
+//
+// For some RGB values:
+// M = max(R, G, B)
+// m = min(R, G, B)
+// m' = mid(R, G, B)
+// chroma = M - m
+// H = m' - m / chroma
+//
+// The relationship H=H' should be preserved, where H and H' are calculated from
+// the RGB and RGB' value at this pixel before and after this tonemapping
+// operation has been applied, respectively.
+static float3 tonemap(float3 rgb) {
+ float3 sorted = clamp(rgb, 0.f, 1.f);
+ float tmp;
+ int permutation = 0;
+
+ // Sort the RGB channels by value
+ if (sorted.z < sorted.y) {
+ tmp = sorted.z;
+ sorted.z = sorted.y;
+ sorted.y = tmp;
+ permutation |= 1;
+ }
+ if (sorted.y < sorted.x) {
+ tmp = sorted.y;
+ sorted.y = sorted.x;
+ sorted.x = tmp;
+ permutation |= 2;
+ }
+ if (sorted.z < sorted.y) {
+ tmp = sorted.z;
+ sorted.z = sorted.y;
+ sorted.y = tmp;
+ permutation |= 4;
+ }
+
+ float2 minmax;
+ minmax.x = sorted.x;
+ minmax.y = sorted.z;
+
+ // Apply tonemapping curve to min, max RGB channel values
+ minmax = native_powr(minmax, 3.f) * toneMapCoeffs.x +
+ native_powr(minmax, 2.f) * toneMapCoeffs.y +
+ minmax * toneMapCoeffs.z + toneMapCoeffs.w;
+
+ // Rescale middle value
+ float newMid;
+ if (sorted.z == sorted.x) {
+ newMid = minmax.y;
+ } else {
+ newMid = minmax.x + ((minmax.y - minmax.x) * (sorted.y - sorted.x) /
+ (sorted.z - sorted.x));
+ }
+
+ float3 finalRGB;
+ switch (permutation) {
+ case 0: // b >= g >= r
+ finalRGB.x = minmax.x;
+ finalRGB.y = newMid;
+ finalRGB.z = minmax.y;
+ break;
+ case 1: // g >= b >= r
+ finalRGB.x = minmax.x;
+ finalRGB.z = newMid;
+ finalRGB.y = minmax.y;
+ break;
+ case 2: // b >= r >= g
+ finalRGB.y = minmax.x;
+ finalRGB.x = newMid;
+ finalRGB.z = minmax.y;
+ break;
+ case 3: // g >= r >= b
+ finalRGB.z = minmax.x;
+ finalRGB.x = newMid;
+ finalRGB.y = minmax.y;
+ break;
+ case 6: // r >= b >= g
+ finalRGB.y = minmax.x;
+ finalRGB.z = newMid;
+ finalRGB.x = minmax.y;
+ break;
+ case 7: // r >= g >= b
+ finalRGB.z = minmax.x;
+ finalRGB.y = newMid;
+ finalRGB.x = minmax.y;
+ break;
+ case 4: // impossible
+ case 5: // impossible
+ default:
+ LOGD("raw_converter.rs: Logic error in tonemap.", 0);
+ break;
+ }
+ return clamp(finalRGB, 0.f, 1.f);
+}
+
+// Apply a colorspace transform to the intermediate colorspace, apply
+// a tonemapping curve, apply a colorspace transform to a final colorspace,
+// and apply a gamma correction curve.
+static float3 applyColorspace(float3 pRGB) {
+ pRGB.x = clamp(pRGB.x, 0.f, neutralPoint.x);
+ pRGB.y = clamp(pRGB.y, 0.f, neutralPoint.y);
+ pRGB.z = clamp(pRGB.z, 0.f, neutralPoint.z);
+
+ float3 intermediate = rsMatrixMultiply(&sensorToIntermediate, pRGB);
+ intermediate = tonemap(intermediate);
+ return gammaCorrectPixel(clamp(rsMatrixMultiply(&intermediateToSRGB, intermediate), 0.f, 1.f));
+}
+
+// Load a 3x3 patch of pixels into the output.
+static void load3x3(uint x, uint y, rs_allocation buf, /*out*/float* outputArray) {
+ outputArray[0] = *((ushort *) rsGetElementAt(buf, x - 1, y - 1));
+ outputArray[1] = *((ushort *) rsGetElementAt(buf, x, y - 1));
+ outputArray[2] = *((ushort *) rsGetElementAt(buf, x + 1, y - 1));
+ outputArray[3] = *((ushort *) rsGetElementAt(buf, x - 1, y));
+ outputArray[4] = *((ushort *) rsGetElementAt(buf, x, y));
+ outputArray[5] = *((ushort *) rsGetElementAt(buf, x + 1, y));
+ outputArray[6] = *((ushort *) rsGetElementAt(buf, x - 1, y + 1));
+ outputArray[7] = *((ushort *) rsGetElementAt(buf, x, y + 1));
+ outputArray[8] = *((ushort *) rsGetElementAt(buf, x + 1, y + 1));
+}
+
+// Blacklevel subtract, and normalize each pixel in the outputArray, and apply the
+// gain map.
+static void linearizeAndGainmap(uint x, uint y, ushort4 blackLevel, int whiteLevel,
+ uint cfa, /*inout*/float* outputArray) {
+ uint kk = 0;
+ for (uint j = y - 1; j <= y + 1; j++) {
+ for (uint i = x - 1; i <= x + 1; i++) {
+ uint index = (i & 1) | ((j & 1) << 1); // bits [0,1] are blacklevel offset
+ index |= (cfa << 2); // bits [2,3] are cfa
+ float bl = 0.f;
+ float g = 1.f;
+ float4 gains = 1.f;
+ if (hasGainMap) {
+ gains = getGain(i, j);
+ }
+ switch (index) {
+ // RGGB
+ case 0:
+ bl = blackLevel.x;
+ g = gains.x;
+ break;
+ case 1:
+ bl = blackLevel.y;
+ g = gains.y;
+ break;
+ case 2:
+ bl = blackLevel.z;
+ g = gains.z;
+ break;
+ case 3:
+ bl = blackLevel.w;
+ g = gains.w;
+ break;
+ // GRBG
+ case 4:
+ bl = blackLevel.x;
+ g = gains.y;
+ break;
+ case 5:
+ bl = blackLevel.y;
+ g = gains.x;
+ break;
+ case 6:
+ bl = blackLevel.z;
+ g = gains.w;
+ break;
+ case 7:
+ bl = blackLevel.w;
+ g = gains.z;
+ break;
+ // GBRG
+ case 8:
+ bl = blackLevel.x;
+ g = gains.y;
+ break;
+ case 9:
+ bl = blackLevel.y;
+ g = gains.w;
+ break;
+ case 10:
+ bl = blackLevel.z;
+ g = gains.x;
+ break;
+ case 11:
+ bl = blackLevel.w;
+ g = gains.z;
+ break;
+ // BGGR
+ case 12:
+ bl = blackLevel.x;
+ g = gains.w;
+ break;
+ case 13:
+ bl = blackLevel.y;
+ g = gains.y;
+ break;
+ case 14:
+ bl = blackLevel.z;
+ g = gains.z;
+ break;
+ case 15:
+ bl = blackLevel.w;
+ g = gains.x;
+ break;
+ }
+ outputArray[kk] = clamp(g * (outputArray[kk] - bl) / (whiteLevel - bl), 0.f, 1.f);
+ kk++;
+ }
+ }
+}
+
+// Apply bilinear-interpolation to demosaic
+static float3 demosaic(uint x, uint y, uint cfa, float* inputArray) {
+ uint index = (x & 1) | ((y & 1) << 1);
+ index |= (cfa << 2);
+ float3 pRGB;
+ switch (index) {
+ case 0:
+ case 5:
+ case 10:
+ case 15: // Red centered
+ // B G B
+ // G R G
+ // B G B
+ pRGB.x = inputArray[4];
+ pRGB.y = (inputArray[1] + inputArray[3] + inputArray[5] + inputArray[7]) / 4;
+ pRGB.z = (inputArray[0] + inputArray[2] + inputArray[6] + inputArray[8]) / 4;
+ break;
+ case 1:
+ case 4:
+ case 11:
+ case 14: // Green centered w/ horizontally adjacent Red
+ // G B G
+ // R G R
+ // G B G
+ pRGB.x = (inputArray[3] + inputArray[5]) / 2;
+ pRGB.y = inputArray[4];
+ pRGB.z = (inputArray[1] + inputArray[7]) / 2;
+ break;
+ case 2:
+ case 7:
+ case 8:
+ case 13: // Green centered w/ horizontally adjacent Blue
+ // G R G
+ // B G B
+ // G R G
+ pRGB.x = (inputArray[1] + inputArray[7]) / 2;
+ pRGB.y = inputArray[4];
+ pRGB.z = (inputArray[3] + inputArray[5]) / 2;
+ break;
+ case 3:
+ case 6:
+ case 9:
+ case 12: // Blue centered
+ // R G R
+ // G B G
+ // R G R
+ pRGB.x = (inputArray[0] + inputArray[2] + inputArray[6] + inputArray[8]) / 4;
+ pRGB.y = (inputArray[1] + inputArray[3] + inputArray[5] + inputArray[7]) / 4;
+ pRGB.z = inputArray[4];
+ break;
+ }
+
+ return pRGB;
+}
+
+// Full RAW->ARGB bitmap conversion kernel
+uchar4 RS_KERNEL convert_RAW_To_ARGB(uint x, uint y) {
+ float3 pRGB;
+ uint xP = x + offsetX;
+ uint yP = y + offsetY;
+ if (xP == 0) xP = 1;
+ if (yP == 0) yP = 1;
+ if (xP == rawWidth - 1) xP = rawWidth - 2;
+ if (yP == rawHeight - 1) yP = rawHeight - 2;
+
+ float patch[9];
+ // TODO: Once ScriptGroup and RS kernels have been updated to allow for iteration over 3x3 pixel
+ // patches, this can be optimized to avoid re-applying the pre-demosaic steps for each pixel,
+ // potentially achieving a 9x speedup here.
+ load3x3(xP, yP, inputRawBuffer, /*out*/ patch);
+ linearizeAndGainmap(xP, yP, blackLevelPattern, whiteLevel, cfaPattern, /*inout*/patch);
+ pRGB = demosaic(xP, yP, cfaPattern, patch);
+
+ return rsPackColorTo8888(applyColorspace(pRGB));
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorTest.java b/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
index c16c135..d606b70 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
@@ -47,6 +47,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class SensorTest extends SensorTestCase {
@@ -329,10 +330,10 @@
Handler handler = new Handler(handlerThread.getLooper());
TestSensorEventListener listener = new TestSensorEventListener(environment, handler);
- sensorManager.registerListener(listener);
- listener.waitForEvents(1);
- sensorManager.requestFlush();
- listener.waitForFlushComplete();
+ CountDownLatch eventLatch = sensorManager.registerListener(listener, 1);
+ listener.waitForEvents(eventLatch, 1);
+ CountDownLatch flushLatch = sensorManager.requestFlush();
+ listener.waitForFlushComplete(flushLatch);
listener.assertEventsReceivedInHandler();
}
@@ -341,26 +342,30 @@
public void testBatchAndFlushWithMutipleSensors() throws Exception {
final int maxSensors = 3;
final int maxReportLatencyUs = (int) TimeUnit.SECONDS.toMicros(10);
- int sensorsCount = mSensorList.size();
- int numSensors = sensorsCount < maxSensors ? sensorsCount : maxSensors;
+ List<Sensor> sensorsToTest = new ArrayList<Sensor>();
+ for (Sensor sensor : mSensorList) {
+ if (sensor.getReportingMode() == Sensor.REPORTING_MODE_CONTINUOUS) {
+ sensorsToTest.add(sensor);
+ if (sensorsToTest.size() == maxSensors) break;
+ }
+ }
+ final int numSensorsToTest = sensorsToTest.size();
+ if (numSensorsToTest == 0) {
+ return;
+ }
StringBuilder builder = new StringBuilder();
ParallelSensorOperation parallelSensorOperation = new ParallelSensorOperation();
- for (int i = 0; i < sensorsCount && numSensors > 0; ++i) {
- Sensor sensor = mSensorList.get(i);
- // skip all non-continuous sensors
- if (sensor.getReportingMode() == Sensor.REPORTING_MODE_CONTINUOUS) {
- TestSensorEnvironment environment = new TestSensorEnvironment(
- getContext(),
- sensor,
- shouldEmulateSensorUnderLoad(),
- SensorManager.SENSOR_DELAY_FASTEST,
- maxReportLatencyUs);
- FlushExecutor executor = new FlushExecutor(environment, 500 /* eventCount */);
- parallelSensorOperation.add(new TestSensorOperation(environment, executor));
- --numSensors;
- builder.append(sensor.getName()).append(", ");
- }
+ for (Sensor sensor : sensorsToTest) {
+ TestSensorEnvironment environment = new TestSensorEnvironment(
+ getContext(),
+ sensor,
+ shouldEmulateSensorUnderLoad(),
+ SensorManager.SENSOR_DELAY_FASTEST,
+ maxReportLatencyUs);
+ FlushExecutor executor = new FlushExecutor(environment, 500 /* eventCount */);
+ parallelSensorOperation.add(new TestSensorOperation(environment, executor));
+ builder.append(sensor.getName()).append(", ");
}
Log.i(TAG, "Testing batch/flush for sensors: " + builder);
@@ -523,12 +528,12 @@
throws InterruptedException {
int sensorReportingMode = mEnvironment.getSensor().getReportingMode();
try {
- sensorManager.registerListener(listener);
+ CountDownLatch eventLatch = sensorManager.registerListener(listener, mEventCount);
if (sensorReportingMode == Sensor.REPORTING_MODE_CONTINUOUS) {
- listener.waitForEvents(mEventCount);
+ listener.waitForEvents(eventLatch, mEventCount);
}
- sensorManager.requestFlush();
- listener.waitForFlushComplete();
+ CountDownLatch flushLatch = sensorManager.requestFlush();
+ listener.waitForFlushComplete(flushLatch);
} finally {
sensorManager.unregisterListener();
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
index a60428f..7b1a499 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
@@ -106,6 +106,31 @@
}
/**
+ * @param eventCount
+ * @return A CountDownLatch initialzed with eventCount and decremented as sensor events arrive
+ * for this listerner.
+ */
+ public CountDownLatch getLatchForSensorEvents(int eventCount) {
+ CountDownLatch latch = new CountDownLatch(eventCount);
+ synchronized (mEventLatches) {
+ mEventLatches.add(latch);
+ }
+ return latch;
+ }
+
+ /**
+ * @return A CountDownLatch initialzed with 1 and decremented as a flush complete arrives
+ * for this listerner.
+ */
+ public CountDownLatch getLatchForFlushCompleteEvent() {
+ CountDownLatch latch = new CountDownLatch(1);
+ synchronized (mFlushLatches) {
+ mFlushLatches.add(latch);
+ }
+ return latch;
+ }
+
+ /**
* {@inheritDoc}
*/
@Override
@@ -182,13 +207,8 @@
*
* @throws AssertionError if there was a timeout after {@link #FLUSH_TIMEOUT_US} µs
*/
- public void waitForFlushComplete() throws InterruptedException {
+ public void waitForFlushComplete(CountDownLatch latch) throws InterruptedException {
clearEvents();
- CountDownLatch latch = new CountDownLatch(1);
- synchronized (mFlushLatches) {
- mFlushLatches.add(latch);
- }
-
try {
String message = SensorCtsHelper.formatAssertionMessage(
"WaitForFlush",
@@ -208,13 +228,8 @@
*
* @throws AssertionError if there was a timeout after {@link #FLUSH_TIMEOUT_US} µs
*/
- public void waitForEvents(int eventCount) throws InterruptedException {
+ public void waitForEvents(CountDownLatch latch, int eventCount) throws InterruptedException {
clearEvents();
- CountDownLatch eventLatch = new CountDownLatch(eventCount);
- synchronized (mEventLatches) {
- mEventLatches.add(eventLatch);
- }
-
try {
long samplingPeriodUs = mEnvironment.getMaximumExpectedSamplingPeriodUs();
// timeout is 2 * event count * expected period + batch timeout + default wait
@@ -224,20 +239,20 @@
long timeoutUs = (2 * eventCount * samplingPeriodUs)
+ mEnvironment.getMaxReportLatencyUs()
+ EVENT_TIMEOUT_US;
- boolean success = eventLatch.await(timeoutUs, TimeUnit.MICROSECONDS);
+ boolean success = latch.await(timeoutUs, TimeUnit.MICROSECONDS);
if (!success) {
String message = SensorCtsHelper.formatAssertionMessage(
"WaitForEvents",
mEnvironment,
"requested=%d, received=%d, timeout=%dus",
eventCount,
- eventCount - eventLatch.getCount(),
+ eventCount - latch.getCount(),
timeoutUs);
Assert.fail(message);
}
} finally {
synchronized (mEventLatches) {
- mEventLatches.remove(eventLatch);
+ mEventLatches.remove(latch);
}
}
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java
index fdd851e..2468bd1 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java
@@ -23,6 +23,8 @@
import android.hardware.SensorManager;
import android.util.Log;
+import java.util.concurrent.CountDownLatch;
+
/**
* A test class that performs the actions of {@link SensorManager} on a single sensor.
* This class allows for a single sensor to be registered and unregistered as well as performing
@@ -85,6 +87,33 @@
}
/**
+ * Register the listener. This method will perform a no-op if the sensor is already registered.
+ *
+ * @return A CountDownLatch initialized with eventCount which is used to wait for sensor
+ * events.
+ * @throws AssertionError if there was an error registering the listener with the
+ * {@link SensorManager}
+ */
+ public CountDownLatch registerListener(TestSensorEventListener listener, int eventCount) {
+ if (mTestSensorEventListener != null) {
+ Log.w(LOG_TAG, "Listener already registered, returning.");
+ return null;
+ }
+
+ CountDownLatch latch = listener.getLatchForSensorEvents(eventCount);
+ mTestSensorEventListener = listener;
+ String message = SensorCtsHelper.formatAssertionMessage("registerListener", mEnvironment);
+ boolean result = mSensorManager.registerListener(
+ mTestSensorEventListener,
+ mEnvironment.getSensor(),
+ mEnvironment.getRequestedSamplingPeriodUs(),
+ mEnvironment.getMaxReportLatencyUs(),
+ mTestSensorEventListener.getHandler());
+ Assert.assertTrue(message, result);
+ return latch;
+ }
+
+ /**
* Unregister the listener. This method will perform a no-op if the sensor is not registered.
*/
public void unregisterListener() {
@@ -101,15 +130,18 @@
* Call {@link SensorManager#flush(SensorEventListener)}. This method will perform a no-op if
* the sensor is not registered.
*
+ * @return A CountDownLatch which can be used to wait for a flush complete event.
* @throws AssertionError if {@link SensorManager#flush(SensorEventListener)} fails.
*/
- public void requestFlush() {
+ public CountDownLatch requestFlush() {
if (mTestSensorEventListener == null) {
Log.w(LOG_TAG, "No listener registered, returning.");
- return;
+ return null;
}
+ CountDownLatch latch = mTestSensorEventListener.getLatchForFlushCompleteEvent();
Assert.assertTrue(
SensorCtsHelper.formatAssertionMessage("Flush", mEnvironment),
mSensorManager.flush(mTestSensorEventListener));
+ return latch;
}
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
index 901216a..3b90b15 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
@@ -41,6 +41,7 @@
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
@@ -212,8 +213,8 @@
public void execute(TestSensorManager sensorManager, TestSensorEventListener listener)
throws InterruptedException {
try {
- sensorManager.registerListener(listener);
- listener.waitForEvents(eventCount);
+ CountDownLatch latch = sensorManager.registerListener(listener, eventCount);
+ listener.waitForEvents(latch, eventCount);
} finally {
sensorManager.unregisterListener();
}
@@ -267,8 +268,8 @@
try {
sensorManager.registerListener(listener);
SensorCtsHelper.sleep(duration, timeUnit);
- sensorManager.requestFlush();
- listener.waitForFlushComplete();
+ CountDownLatch latch = sensorManager.requestFlush();
+ listener.waitForFlushComplete(latch);
} finally {
sensorManager.unregisterListener();
}
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyChainTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyChainTest.java
index db6d6ba..7aa6a9d 100644
--- a/tests/tests/keystore/src/android/keystore/cts/KeyChainTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyChainTest.java
@@ -16,10 +16,11 @@
package android.keystore.cts;
+import android.content.pm.PackageManager;
import android.security.KeyChain;
-import junit.framework.TestCase;
+import android.test.AndroidTestCase;
-public class KeyChainTest extends TestCase {
+public class KeyChainTest extends AndroidTestCase {
public void testIsKeyAlgorithmSupported_RequiredAlgorithmsSupported() throws Exception {
assertTrue("DSA must be supported", KeyChain.isKeyAlgorithmSupported("DSA"));
assertTrue("EC must be supported", KeyChain.isKeyAlgorithmSupported("EC"));
@@ -34,11 +35,21 @@
* tests in hardware/libhardware/tests/keymaster/
*/
public void testIsBoundKeyAlgorithm_RequiredAlgorithmsSupported() throws Exception {
- assertTrue("RSA must be hardware-backed by a hardware-specific Keymaster HAL",
- KeyChain.isBoundKeyAlgorithm("RSA"));
+ if (isLeanbackOnly()) {
+ KeyChain.isBoundKeyAlgorithm("RSA");
+ }
+ else {
+ assertTrue("RSA must be hardware-backed by a hardware-specific Keymaster HAL",
+ KeyChain.isBoundKeyAlgorithm("RSA"));
+ }
// These are not required, but must not throw an exception
KeyChain.isBoundKeyAlgorithm("DSA");
KeyChain.isBoundKeyAlgorithm("EC");
}
+
+ private boolean isLeanbackOnly() {
+ PackageManager pm = getContext().getPackageManager();
+ return (pm != null && pm.hasSystemFeature("android.software.leanback_only"));
+ }
}
diff --git a/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java b/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java
index 5f326ee..96150ca 100644
--- a/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java
+++ b/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java
@@ -62,7 +62,8 @@
private static final long WAIT_FOR_IMAGE_TIMEOUT_MS = 1000;
private static final String DEBUG_FILE_NAME_BASE = "/sdcard/";
private static final int NUM_FRAME_DECODED = 100;
- private static final int MAX_NUM_IMAGES = 3;
+ // video decoders only support a single outstanding image with the consumer
+ private static final int MAX_NUM_IMAGES = 1;
private Resources mResources;
private MediaCodec.BufferInfo mBufferInfo = new MediaCodec.BufferInfo();
diff --git a/tests/tests/media/src/android/media/cts/PostProcTestBase.java b/tests/tests/media/src/android/media/cts/PostProcTestBase.java
index c31693e..ef87662 100644
--- a/tests/tests/media/src/android/media/cts/PostProcTestBase.java
+++ b/tests/tests/media/src/android/media/cts/PostProcTestBase.java
@@ -31,6 +31,8 @@
protected Looper mLooper = null;
protected final Object mLock = new Object();
protected int mChangedParameter = -1;
+ protected final static String BUNDLE_VOLUME_EFFECT_UUID =
+ "119341a0-8469-11df-81f9-0002a5d5c51b";
protected boolean hasAudioOutput() {
return getContext().getPackageManager().hasSystemFeature(
diff --git a/tests/tests/media/src/android/media/cts/PresentationSyncTest.java b/tests/tests/media/src/android/media/cts/PresentationSyncTest.java
index ac86f1e..efa2e0a 100644
--- a/tests/tests/media/src/android/media/cts/PresentationSyncTest.java
+++ b/tests/tests/media/src/android/media/cts/PresentationSyncTest.java
@@ -109,7 +109,20 @@
CpuWaster cpuWaster = new CpuWaster();
try {
cpuWaster.start();
- runThroughputTest(output, refreshNsec, 0.75f);
+ // Tests with mult < 1.0f are flaky, for two reasons:
+ //
+ // (a) They assume that the GPU can render the test scene in less than mult*refreshNsec.
+ // It's a simple scene, but CTS/CDD don't currently require being able to do more
+ // than a full-screen clear in refreshNsec.
+ //
+ // (b) More importantly, it assumes that the only rate-limiting happening is
+ // backpressure from the buffer queue. If the EGL implementation is doing its own
+ // rate-limiting (to limit the amount of work queued to the GPU at any time), then
+ // depending on how that's implemented the buffer queue may not have two frames
+ // pending very often. So the consumer won't be able to drop many frames, and the
+ // throughput won't be much better than with mult=1.0.
+ //
+ // runThroughputTest(output, refreshNsec, 0.75f);
cpuWaster.stop();
runThroughputTest(output, refreshNsec, 1.0f);
runThroughputTest(output, refreshNsec, 2.0f);
diff --git a/tests/tests/media/src/android/media/cts/VideoEncoderTest.java b/tests/tests/media/src/android/media/cts/VideoEncoderTest.java
index 616e713..858e47c 100644
--- a/tests/tests/media/src/android/media/cts/VideoEncoderTest.java
+++ b/tests/tests/media/src/android/media/cts/VideoEncoderTest.java
@@ -758,7 +758,8 @@
final private Map<Size, Set<Size>> mMinMax; // extreme sizes
final private Map<Size, Set<Size>> mNearMinMax; // sizes near extreme
- final private Set<Size> mArbitrary; // arbitrary sizes in the middle
+ final private Set<Size> mArbitraryW; // arbitrary widths in the middle
+ final private Set<Size> mArbitraryH; // arbitrary heights in the middle
final private Set<Size> mSizes; // all non-specifically tested sizes
final private int xAlign;
@@ -772,7 +773,8 @@
/* calculate min/max sizes */
mMinMax = new HashMap<Size, Set<Size>>();
mNearMinMax = new HashMap<Size, Set<Size>>();
- mArbitrary = new HashSet<Size>();
+ mArbitraryW = new HashSet<Size>();
+ mArbitraryH = new HashSet<Size>();
mSizes = new HashSet<Size>();
xAlign = mCaps.getWidthAlignment();
@@ -791,18 +793,28 @@
// initialize arbitrary sizes
for (int i = 1; i <= 7; ++i) {
int j = ((7 * i) % 11) + 1;
- int width = alignedPointInRange(i * 0.125, xAlign, mCaps.getSupportedWidths());
- int height = alignedPointInRange(
- j * 0.077, yAlign, mCaps.getSupportedHeightsFor(width));
- mArbitrary.add(new Size(width, height));
+ int width, height;
+ try {
+ width = alignedPointInRange(i * 0.125, xAlign, mCaps.getSupportedWidths());
+ height = alignedPointInRange(
+ j * 0.077, yAlign, mCaps.getSupportedHeightsFor(width));
+ mArbitraryW.add(new Size(width, height));
+ } catch (IllegalArgumentException e) {
+ }
- height = alignedPointInRange(i * 0.125, yAlign, mCaps.getSupportedHeights());
- width = alignedPointInRange(j * 0.077, xAlign, mCaps.getSupportedWidthsFor(height));
- mArbitrary.add(new Size(width, height));
+ try {
+ height = alignedPointInRange(i * 0.125, yAlign, mCaps.getSupportedHeights());
+ width = alignedPointInRange(j * 0.077, xAlign, mCaps.getSupportedWidthsFor(height));
+ mArbitraryH.add(new Size(width, height));
+ } catch (IllegalArgumentException e) {
+ }
}
- mArbitrary.removeAll(mSizes);
- mSizes.addAll(mArbitrary);
- if (DEBUG) Log.i(TAG, "arbitrary=" + mArbitrary);
+ mArbitraryW.removeAll(mArbitraryH);
+ mArbitraryW.removeAll(mSizes);
+ mSizes.addAll(mArbitraryW);
+ mArbitraryH.removeAll(mSizes);
+ mSizes.addAll(mArbitraryH);
+ if (DEBUG) Log.i(TAG, "arbitrary=" + mArbitraryW + "/" + mArbitraryH);
}
private void addExtremeSizesFor(int x, int y) {
@@ -904,9 +916,9 @@
return !skipped;
}
- public boolean testArbitrary(boolean flexYUV) {
+ public boolean testArbitrary(boolean flexYUV, boolean widths) {
boolean skipped = true;
- for (Size s : mArbitrary) {
+ for (Size s : (widths ? mArbitraryW : mArbitraryH)) {
if (test(s.getWidth(), s.getHeight(), false /* optional */, flexYUV)) {
skipped = false;
}
@@ -1002,11 +1014,14 @@
|| info.getName().toLowerCase().startsWith("omx.google.") != goog) {
continue;
}
+ CodecCapabilities caps = null;
try {
- CodecCapabilities caps = info.getCapabilitiesForType(mime);
- result.add(new Encoder(info.getName(), mime, caps));
+ caps = info.getCapabilitiesForType(mime);
} catch (IllegalArgumentException e) { // mime is not supported
+ continue;
}
+ assertNotNull(info.getName() + " capabilties for " + mime + " returned null", caps);
+ result.add(new Encoder(info.getName(), mime, caps));
}
return result.toArray(new Encoder[result.size()]);
}
@@ -1219,31 +1234,57 @@
public void testOtherVP9FlexNearMaxMax() { nearmaxmax(otherVP9(), true /* flex */); }
public void testOtherVP9SurfNearMaxMax() { nearmaxmax(otherVP9(), false /* flex */); }
- public void testGoogH265FlexArbitrary() { arbitrary(googH265(), true /* flex */); }
- public void testGoogH265SurfArbitrary() { arbitrary(googH265(), false /* flex */); }
- public void testGoogH264FlexArbitrary() { arbitrary(googH264(), true /* flex */); }
- public void testGoogH264SurfArbitrary() { arbitrary(googH264(), false /* flex */); }
- public void testGoogH263FlexArbitrary() { arbitrary(googH263(), true /* flex */); }
- public void testGoogH263SurfArbitrary() { arbitrary(googH263(), false /* flex */); }
- public void testGoogMpeg4FlexArbitrary() { arbitrary(googMpeg4(), true /* flex */); }
- public void testGoogMpeg4SurfArbitrary() { arbitrary(googMpeg4(), false /* flex */); }
- public void testGoogVP8FlexArbitrary() { arbitrary(googVP8(), true /* flex */); }
- public void testGoogVP8SurfArbitrary() { arbitrary(googVP8(), false /* flex */); }
- public void testGoogVP9FlexArbitrary() { arbitrary(googVP9(), true /* flex */); }
- public void testGoogVP9SurfArbitrary() { arbitrary(googVP9(), false /* flex */); }
+ public void testGoogH265FlexArbitraryW() { arbitraryw(googH265(), true /* flex */); }
+ public void testGoogH265SurfArbitraryW() { arbitraryw(googH265(), false /* flex */); }
+ public void testGoogH264FlexArbitraryW() { arbitraryw(googH264(), true /* flex */); }
+ public void testGoogH264SurfArbitraryW() { arbitraryw(googH264(), false /* flex */); }
+ public void testGoogH263FlexArbitraryW() { arbitraryw(googH263(), true /* flex */); }
+ public void testGoogH263SurfArbitraryW() { arbitraryw(googH263(), false /* flex */); }
+ public void testGoogMpeg4FlexArbitraryW() { arbitraryw(googMpeg4(), true /* flex */); }
+ public void testGoogMpeg4SurfArbitraryW() { arbitraryw(googMpeg4(), false /* flex */); }
+ public void testGoogVP8FlexArbitraryW() { arbitraryw(googVP8(), true /* flex */); }
+ public void testGoogVP8SurfArbitraryW() { arbitraryw(googVP8(), false /* flex */); }
+ public void testGoogVP9FlexArbitraryW() { arbitraryw(googVP9(), true /* flex */); }
+ public void testGoogVP9SurfArbitraryW() { arbitraryw(googVP9(), false /* flex */); }
- public void testOtherH265FlexArbitrary() { arbitrary(otherH265(), true /* flex */); }
- public void testOtherH265SurfArbitrary() { arbitrary(otherH265(), false /* flex */); }
- public void testOtherH264FlexArbitrary() { arbitrary(otherH264(), true /* flex */); }
- public void testOtherH264SurfArbitrary() { arbitrary(otherH264(), false /* flex */); }
- public void testOtherH263FlexArbitrary() { arbitrary(otherH263(), true /* flex */); }
- public void testOtherH263SurfArbitrary() { arbitrary(otherH263(), false /* flex */); }
- public void testOtherMpeg4FlexArbitrary() { arbitrary(otherMpeg4(), true /* flex */); }
- public void testOtherMpeg4SurfArbitrary() { arbitrary(otherMpeg4(), false /* flex */); }
- public void testOtherVP8FlexArbitrary() { arbitrary(otherVP8(), true /* flex */); }
- public void testOtherVP8SurfArbitrary() { arbitrary(otherVP8(), false /* flex */); }
- public void testOtherVP9FlexArbitrary() { arbitrary(otherVP9(), true /* flex */); }
- public void testOtherVP9SurfArbitrary() { arbitrary(otherVP9(), false /* flex */); }
+ public void testOtherH265FlexArbitraryW() { arbitraryw(otherH265(), true /* flex */); }
+ public void testOtherH265SurfArbitraryW() { arbitraryw(otherH265(), false /* flex */); }
+ public void testOtherH264FlexArbitraryW() { arbitraryw(otherH264(), true /* flex */); }
+ public void testOtherH264SurfArbitraryW() { arbitraryw(otherH264(), false /* flex */); }
+ public void testOtherH263FlexArbitraryW() { arbitraryw(otherH263(), true /* flex */); }
+ public void testOtherH263SurfArbitraryW() { arbitraryw(otherH263(), false /* flex */); }
+ public void testOtherMpeg4FlexArbitraryW() { arbitraryw(otherMpeg4(), true /* flex */); }
+ public void testOtherMpeg4SurfArbitraryW() { arbitraryw(otherMpeg4(), false /* flex */); }
+ public void testOtherVP8FlexArbitraryW() { arbitraryw(otherVP8(), true /* flex */); }
+ public void testOtherVP8SurfArbitraryW() { arbitraryw(otherVP8(), false /* flex */); }
+ public void testOtherVP9FlexArbitraryW() { arbitraryw(otherVP9(), true /* flex */); }
+ public void testOtherVP9SurfArbitraryW() { arbitraryw(otherVP9(), false /* flex */); }
+
+ public void testGoogH265FlexArbitraryH() { arbitraryh(googH265(), true /* flex */); }
+ public void testGoogH265SurfArbitraryH() { arbitraryh(googH265(), false /* flex */); }
+ public void testGoogH264FlexArbitraryH() { arbitraryh(googH264(), true /* flex */); }
+ public void testGoogH264SurfArbitraryH() { arbitraryh(googH264(), false /* flex */); }
+ public void testGoogH263FlexArbitraryH() { arbitraryh(googH263(), true /* flex */); }
+ public void testGoogH263SurfArbitraryH() { arbitraryh(googH263(), false /* flex */); }
+ public void testGoogMpeg4FlexArbitraryH() { arbitraryh(googMpeg4(), true /* flex */); }
+ public void testGoogMpeg4SurfArbitraryH() { arbitraryh(googMpeg4(), false /* flex */); }
+ public void testGoogVP8FlexArbitraryH() { arbitraryh(googVP8(), true /* flex */); }
+ public void testGoogVP8SurfArbitraryH() { arbitraryh(googVP8(), false /* flex */); }
+ public void testGoogVP9FlexArbitraryH() { arbitraryh(googVP9(), true /* flex */); }
+ public void testGoogVP9SurfArbitraryH() { arbitraryh(googVP9(), false /* flex */); }
+
+ public void testOtherH265FlexArbitraryH() { arbitraryh(otherH265(), true /* flex */); }
+ public void testOtherH265SurfArbitraryH() { arbitraryh(otherH265(), false /* flex */); }
+ public void testOtherH264FlexArbitraryH() { arbitraryh(otherH264(), true /* flex */); }
+ public void testOtherH264SurfArbitraryH() { arbitraryh(otherH264(), false /* flex */); }
+ public void testOtherH263FlexArbitraryH() { arbitraryh(otherH263(), true /* flex */); }
+ public void testOtherH263SurfArbitraryH() { arbitraryh(otherH263(), false /* flex */); }
+ public void testOtherMpeg4FlexArbitraryH() { arbitraryh(otherMpeg4(), true /* flex */); }
+ public void testOtherMpeg4SurfArbitraryH() { arbitraryh(otherMpeg4(), false /* flex */); }
+ public void testOtherVP8FlexArbitraryH() { arbitraryh(otherVP8(), true /* flex */); }
+ public void testOtherVP8SurfArbitraryH() { arbitraryh(otherVP8(), false /* flex */); }
+ public void testOtherVP9FlexArbitraryH() { arbitraryh(otherVP9(), true /* flex */); }
+ public void testOtherVP9SurfArbitraryH() { arbitraryh(otherVP9(), false /* flex */); }
public void testGoogH265FlexQCIF() { specific(googH265(), 176, 144, true /* flex */); }
public void testGoogH265SurfQCIF() { specific(googH265(), 176, 144, false /* flex */); }
@@ -1483,14 +1524,14 @@
}
}
- private void arbitrary(Encoder[] encoders, boolean flexYUV) {
+ private void arbitrary(Encoder[] encoders, boolean flexYUV, boolean widths) {
boolean skipped = true;
if (encoders.length == 0) {
MediaUtils.skipTest("no such encoder present");
return;
}
for (Encoder encoder: encoders) {
- if (encoder.testArbitrary(flexYUV)) {
+ if (encoder.testArbitrary(flexYUV, widths)) {
skipped = false;
}
}
@@ -1499,6 +1540,14 @@
}
}
+ private void arbitraryw(Encoder[] encoders, boolean flexYUV) {
+ arbitrary(encoders, flexYUV, true /* widths */);
+ }
+
+ private void arbitraryh(Encoder[] encoders, boolean flexYUV) {
+ arbitrary(encoders, flexYUV, false /* widths */);
+ }
+
/* test specific size */
private void specific(Encoder[] encoders, int width, int height, boolean flexYUV) {
boolean skipped = true;
diff --git a/tests/tests/media/src/android/media/cts/VisualizerTest.java b/tests/tests/media/src/android/media/cts/VisualizerTest.java
index 73e48f2..ea7a13f 100644
--- a/tests/tests/media/src/android/media/cts/VisualizerTest.java
+++ b/tests/tests/media/src/android/media/cts/VisualizerTest.java
@@ -27,6 +27,7 @@
import android.media.audiofx.Visualizer.MeasurementPeakRms;
import android.os.Looper;
import android.test.AndroidTestCase;
+import java.util.UUID;
import android.util.Log;
public class VisualizerTest extends PostProcTestBase {
@@ -281,6 +282,7 @@
//Test case 4.1: test measurement of peak / RMS
public void test4_1MeasurePeakRms() throws Exception {
+ AudioEffect vc = null;
try {
// this test will play a 1kHz sine wave with peaks at -40dB
MediaPlayer mp = MediaPlayer.create(getContext(), R.raw.sine1khzm40db);
@@ -289,6 +291,15 @@
final int MAX_MEASUREMENT_ERROR_MB = 2000;
assertNotNull("null MediaPlayer", mp);
+ // creating a volume controller on output mix ensures that ro.audio.silent mutes
+ // audio after the effects and not before
+ vc = new AudioEffect(
+ AudioEffect.EFFECT_TYPE_NULL,
+ UUID.fromString(BUNDLE_VOLUME_EFFECT_UUID),
+ 0,
+ mp.getAudioSessionId());
+ vc.setEnabled(true);
+
AudioManager am = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
assertNotNull("null AudioManager", am);
int originalVolume = am.getStreamVolume(AudioManager.STREAM_MUSIC);
@@ -308,7 +319,7 @@
int currentPosition = mp.getCurrentPosition();
final int maxTry = 100;
int tryCount = 0;
- while (currentPosition < 400 && tryCount < maxTry) {
+ while (currentPosition < 200 && tryCount < maxTry) {
Thread.sleep(50);
currentPosition = mp.getCurrentPosition();
tryCount++;
@@ -333,12 +344,15 @@
} catch (InterruptedException e) {
fail("sleep() interrupted");
} finally {
+ if (vc != null)
+ vc.release();
releaseVisualizer();
}
}
//Test case 4.2: test measurement of peak / RMS in Long MP3
public void test4_2MeasurePeakRmsLongMP3() throws Exception {
+ AudioEffect vc = null;
try {
// this test will play a 1kHz sine wave with peaks at -40dB
MediaPlayer mp = MediaPlayer.create(getContext(), R.raw.sine1khzs40dblong);
@@ -347,6 +361,15 @@
final int MAX_MEASUREMENT_ERROR_MB = 2000;
assertNotNull("null MediaPlayer", mp);
+ // creating a volume controller on output mix ensures that ro.audio.silent mutes
+ // audio after the effects and not before
+ vc = new AudioEffect(
+ AudioEffect.EFFECT_TYPE_NULL,
+ UUID.fromString(BUNDLE_VOLUME_EFFECT_UUID),
+ 0,
+ mp.getAudioSessionId());
+ vc.setEnabled(true);
+
AudioManager am = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
assertNotNull("null AudioManager", am);
int originalVolume = am.getStreamVolume(AudioManager.STREAM_MUSIC);
@@ -390,6 +413,8 @@
} catch (InterruptedException e) {
fail("sleep() interrupted");
} finally {
+ if (vc != null)
+ vc.release();
releaseVisualizer();
}
}
diff --git a/tests/tests/net/src/android/net/ipv6/cts/PingTest.java b/tests/tests/net/src/android/net/ipv6/cts/PingTest.java
index e9bfb43..eddb416 100644
--- a/tests/tests/net/src/android/net/ipv6/cts/PingTest.java
+++ b/tests/tests/net/src/android/net/ipv6/cts/PingTest.java
@@ -53,6 +53,9 @@
/** Maximum size of the packets we're using to test. */
private static final int MAX_SIZE = 4096;
+ /** Size of the ICMPv6 header. */
+ private static final int ICMP_HEADER_SIZE = 8;
+
/** Number of packets to test. */
private static final int NUM_PACKETS = 10;
@@ -65,7 +68,7 @@
* Returns a byte array containing an ICMPv6 echo request with the specified payload length.
*/
private byte[] pingPacket(int payloadLength) {
- byte[] packet = new byte[payloadLength + 8];
+ byte[] packet = new byte[payloadLength + ICMP_HEADER_SIZE];
new Random().nextBytes(packet);
System.arraycopy(PING_HEADER, 0, packet, 0, PING_HEADER.length);
return packet;
@@ -155,7 +158,7 @@
assertEquals("localhost/::1", ipv6Loopback.toString());
for (int i = 0; i < NUM_PACKETS; i++) {
- byte[] packet = pingPacket((int) (Math.random() * MAX_SIZE));
+ byte[] packet = pingPacket((int) (Math.random() * (MAX_SIZE - ICMP_HEADER_SIZE)));
FileDescriptor s = createPingSocket();
// Use both recvfrom and read().
sendPing(s, ipv6Loopback, packet);
diff --git a/tests/tests/uirendering/res/layout/blue_padded_layout.xml b/tests/tests/uirendering/res/layout/blue_padded_layout.xml
index 68c9cd1..2bfd049 100644
--- a/tests/tests/uirendering/res/layout/blue_padded_layout.xml
+++ b/tests/tests/uirendering/res/layout/blue_padded_layout.xml
@@ -14,16 +14,15 @@
limitations under the License.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/test_root"
- android:layout_width="200px"
- android:layout_height="200px"
- android:clipChildren="false">
+ android:layout_width="@dimen/test_width"
+ android:layout_height="@dimen/test_height"
+ android:clipChildren="false">
<android.uirendering.cts.testclasses.view.UnclippedBlueView
- android:id="@+id/child"
- android:layout_width="100px"
- android:layout_height="100px"
- android:paddingLeft="15px"
- android:paddingTop="16px"
- android:paddingRight="17px"
- android:paddingBottom="18px"/>
+ android:id="@+id/child"
+ android:layout_width="80px"
+ android:layout_height="80px"
+ android:paddingLeft="15px"
+ android:paddingTop="16px"
+ android:paddingRight="17px"
+ android:paddingBottom="18px"/>
</FrameLayout>
diff --git a/tests/tests/uirendering/res/layout/blue_padded_square.xml b/tests/tests/uirendering/res/layout/blue_padded_square.xml
index 71f4b0c..7d867d9 100644
--- a/tests/tests/uirendering/res/layout/blue_padded_square.xml
+++ b/tests/tests/uirendering/res/layout/blue_padded_square.xml
@@ -14,7 +14,6 @@
limitations under the License.
-->
<View xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/test_root"
- android:layout_width="100px"
- android:layout_height="100px"
+ android:layout_width="@dimen/test_width"
+ android:layout_height="@dimen/test_height"
android:background="@drawable/blue_padded_square"/>
diff --git a/tests/tests/uirendering/res/layout/draw_activity_view.xml b/tests/tests/uirendering/res/layout/draw_activity_view.xml
deleted file mode 100644
index 54a72e3..0000000
--- a/tests/tests/uirendering/res/layout/draw_activity_view.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 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.
--->
-
-<android.uirendering.cts.CanvasClientView xmlns:android="http://schemas.android.com/apk/res/android"/>
diff --git a/tests/tests/uirendering/res/layout/simple_rect_layout.xml b/tests/tests/uirendering/res/layout/simple_rect_layout.xml
index b570df8..7d6f928 100644
--- a/tests/tests/uirendering/res/layout/simple_rect_layout.xml
+++ b/tests/tests/uirendering/res/layout/simple_rect_layout.xml
@@ -13,14 +13,12 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/test_root"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <View android:layout_width="100px"
- android:layout_height="100px"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="@dimen/test_width"
+ android:layout_height="@dimen/test_height">
+ <View android:layout_width="80px"
+ android:layout_height="80px"
+ android:translationX="5px"
+ android:translationY="5px"
android:background="#00f" />
-
-</LinearLayout>
+</FrameLayout>
diff --git a/tests/tests/uirendering/res/layout/simple_red_layout.xml b/tests/tests/uirendering/res/layout/simple_red_layout.xml
index 2d2d189..2af8db6 100644
--- a/tests/tests/uirendering/res/layout/simple_red_layout.xml
+++ b/tests/tests/uirendering/res/layout/simple_red_layout.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
<View xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="@dimen/test_width"
+ android:layout_height="@dimen/test_height"
android:id="@+id/test_root"
- android:layout_width="180px"
- android:layout_height="180px"
android:background="#f00" />
diff --git a/tests/tests/uirendering/res/layout/test_container.xml b/tests/tests/uirendering/res/layout/test_container.xml
new file mode 100644
index 0000000..94a8eab
--- /dev/null
+++ b/tests/tests/uirendering/res/layout/test_container.xml
@@ -0,0 +1,24 @@
+<!-- Copyright (C) 2015 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.
+ -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/test_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <ViewStub
+ android:id="@+id/test_content_stub"
+ android:layout_gravity="center"
+ android:layout_width="@dimen/test_width"
+ android:layout_height="@dimen/test_height"/>
+</FrameLayout>
diff --git a/tests/tests/uirendering/res/layout/test_content_canvasclientview.xml b/tests/tests/uirendering/res/layout/test_content_canvasclientview.xml
new file mode 100644
index 0000000..9f8a139
--- /dev/null
+++ b/tests/tests/uirendering/res/layout/test_content_canvasclientview.xml
@@ -0,0 +1,18 @@
+<!-- Copyright (C) 2015 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.
+ -->
+<android.uirendering.cts.testinfrastructure.CanvasClientView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="@dimen/test_width"
+ android:layout_height="@dimen/test_height"/>
\ No newline at end of file
diff --git a/tests/tests/uirendering/res/layout/test_content_webview.xml b/tests/tests/uirendering/res/layout/test_content_webview.xml
new file mode 100644
index 0000000..8b03cab
--- /dev/null
+++ b/tests/tests/uirendering/res/layout/test_content_webview.xml
@@ -0,0 +1,18 @@
+<!-- Copyright (C) 2015 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.
+ -->
+<WebView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="@dimen/test_width"
+ android:layout_height="@dimen/test_height"/>
\ No newline at end of file
diff --git a/tests/tests/uirendering/res/values/dimens.xml b/tests/tests/uirendering/res/values/dimens.xml
new file mode 100644
index 0000000..1c304d3
--- /dev/null
+++ b/tests/tests/uirendering/res/values/dimens.xml
@@ -0,0 +1,18 @@
+<!-- Copyright (C) 2015 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.
+ -->
+<resources>
+ <dimen name="test_width">90px</dimen>
+ <dimen name="test_height">90px</dimen>
+</resources>
diff --git a/tests/tests/uirendering/res/values/themes.xml b/tests/tests/uirendering/res/values/themes.xml
index 751b7cb..f08f029 100644
--- a/tests/tests/uirendering/res/values/themes.xml
+++ b/tests/tests/uirendering/res/values/themes.xml
@@ -17,9 +17,9 @@
<style name="WhiteBackgroundTheme" parent="@android:style/Theme.Holo.NoActionBar.Fullscreen">
<item name="android:windowNoTitle">true</item>
<item name="android:windowFullscreen">true</item>
+ <item name="android:windowOverscan">true</item>
<item name="android:fadingEdge">none</item>
<item name="android:windowBackground">@android:color/white</item>
- <!--This shouldn't be necessary currently a hack for an existing bug with transitions-->
<item name="android:windowContentTransitions">false</item>
<item name="android:windowAnimationStyle">@null</item>
</style>
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/ColorVerifier.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/ColorVerifier.java
index ea836ea..7f62b3e 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/ColorVerifier.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/ColorVerifier.java
@@ -22,11 +22,16 @@
private int mColor;
public ColorVerifier(int color) {
- this(color, 20);
+ this(color, DEFAULT_THRESHOLD);
}
- public ColorVerifier(int color, int threshold) {
- super(threshold);
+ public ColorVerifier(int color, int colorTolerance) {
+ super(colorTolerance);
+ mColor = color;
+ }
+
+ public ColorVerifier(int color, int colorThreshold, float spatialTolerance) {
+ super(colorThreshold, spatialTolerance);
mColor = color;
}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/GoldenImageVerifier.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/GoldenImageVerifier.java
index 672b3f6..d4a63de 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/GoldenImageVerifier.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/GoldenImageVerifier.java
@@ -18,6 +18,7 @@
import android.graphics.Bitmap;
import android.uirendering.cts.bitmapcomparers.BitmapComparer;
import android.uirendering.cts.differencevisualizers.PassFailVisualizer;
+import android.uirendering.cts.testinfrastructure.ActivityTestBase;
public class GoldenImageVerifier extends BitmapVerifier {
private BitmapComparer mBitmapComparer;
@@ -26,7 +27,7 @@
public GoldenImageVerifier(Bitmap goldenBitmap, BitmapComparer bitmapComparer) {
mGoldenBitmapArray = new int[goldenBitmap.getWidth() * goldenBitmap.getHeight()];
goldenBitmap.getPixels(mGoldenBitmapArray, 0, goldenBitmap.getWidth(), 0, 0,
- goldenBitmap.getWidth(), goldenBitmap.getHeight());
+ ActivityTestBase.TEST_WIDTH, ActivityTestBase.TEST_HEIGHT);
mBitmapComparer = bitmapComparer;
}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/PerPixelBitmapVerifier.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/PerPixelBitmapVerifier.java
index 0bdcc9b..2d00db5 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/PerPixelBitmapVerifier.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/PerPixelBitmapVerifier.java
@@ -26,38 +26,54 @@
*/
public abstract class PerPixelBitmapVerifier extends BitmapVerifier {
private static final String TAG = "PerPixelBitmapVerifer";
- private int mTolerance;
+ public static final int DEFAULT_THRESHOLD = 48;
- public PerPixelBitmapVerifier(int tolerance) {
- mTolerance = tolerance;
+ // total color difference tolerated without the pixel failing
+ private int mColorTolerance;
+
+ // portion of bitmap allowed to fail pixel check
+ private float mSpatialTolerance;
+
+ public PerPixelBitmapVerifier() {
+ this(DEFAULT_THRESHOLD, 0);
+ }
+
+ public PerPixelBitmapVerifier(int colorTolerance) {
+ this(colorTolerance, 0);
+ }
+
+ public PerPixelBitmapVerifier(int colorTolerance, float spatialTolerance) {
+ mColorTolerance = colorTolerance;
+ mSpatialTolerance = spatialTolerance;
}
protected int getExpectedColor(int x, int y) {
return Color.WHITE;
}
-
public boolean verify(int[] bitmap, int offset, int stride, int width, int height) {
- int failCount = 0;
+ int failures = 0;
int[] differenceMap = new int[bitmap.length];
for (int y = 0 ; y < height ; y++) {
for (int x = 0 ; x < width ; x++) {
int index = indexFromXAndY(x, y, stride, offset);
- int expectedColor = getExpectedColor(x, y);
- if (!verifyPixel(bitmap[index], expectedColor)) {
- if (failCount < 50) {
- Log.d(TAG, "Expected : " + Integer.toHexString(expectedColor)
+ if (!verifyPixel(x, y, bitmap[index])) {
+ if (failures < 50) {
+ Log.d(TAG, "Expected : " + Integer.toHexString(getExpectedColor(x, y))
+ " received : " + Integer.toHexString(bitmap[index])
+ " at position (" + x + "," + y + ")");
}
- failCount++;
+ failures++;
differenceMap[index] = FAIL_COLOR;
} else {
differenceMap[index] = PASS_COLOR;
}
}
}
- boolean success = failCount == 0;
+ int toleratedFailures = (int) (mSpatialTolerance * width * height);
+ boolean success = failures <= toleratedFailures;
+ Log.d(TAG, failures + " failures observed out of "
+ + toleratedFailures + " tolerated failures");
if (!success) {
mDifferenceBitmap = Bitmap.createBitmap(ActivityTestBase.TEST_WIDTH,
ActivityTestBase.TEST_HEIGHT, Bitmap.Config.ARGB_8888);
@@ -67,7 +83,9 @@
return success;
}
- protected boolean verifyPixel(int color, int expectedColor) {
- return CompareUtils.verifyPixelWithThreshold(color, expectedColor, mTolerance);
+
+ protected boolean verifyPixel(int x, int y, int observedColor) {
+ int expectedColor = getExpectedColor(x, y);
+ return CompareUtils.verifyPixelWithThreshold(observedColor, expectedColor, mColorTolerance);
}
}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/RectVerifier.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/RectVerifier.java
index 06a430b..f4bece1 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/RectVerifier.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/RectVerifier.java
@@ -26,7 +26,7 @@
private Rect mInnerRect;
public RectVerifier(int outerColor, int innerColor, Rect innerRect) {
- this(outerColor, innerColor, innerRect, 20);
+ this(outerColor, innerColor, innerRect, DEFAULT_THRESHOLD);
}
public RectVerifier(int outerColor, int innerColor, Rect innerRect, int tolerance) {
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/BitmapFilterTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/BitmapFilterTests.java
index ddae100..117fe17 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/BitmapFilterTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/BitmapFilterTests.java
@@ -28,7 +28,6 @@
import android.uirendering.cts.testinfrastructure.CanvasClient;
public class BitmapFilterTests extends ActivityTestBase {
- private static final int THRESHOLD = 20;
private static final int WHITE_WEIGHT = 255 * 3;
private enum FilterEnum {
// Creates Paint object that will have bitmap filtering
@@ -40,72 +39,74 @@
ADD_FILTER
}
- /**
- * Verifies that a Bitmap only contains white and black, within a certain threshold
- */
- private static BitmapVerifier mBlackWhiteVerifier = new PerPixelBitmapVerifier(THRESHOLD) {
+ private static final BitmapVerifier BLACK_WHITE_ONLY_VERIFIER
+ = new PerPixelBitmapVerifier(PerPixelBitmapVerifier.DEFAULT_THRESHOLD, 0.99f) {
@Override
- protected boolean verifyPixel(int color, int expectedColor) {
+ protected boolean verifyPixel(int x, int y, int color) {
int weight = Color.red(color) + Color.blue(color) + Color.green(color);
- return weight < THRESHOLD // is approx Color.BLACK
- || weight > WHITE_WEIGHT - THRESHOLD; // is approx Color.WHITE
+ return weight < DEFAULT_THRESHOLD // is approx Color.BLACK
+ || weight > WHITE_WEIGHT - DEFAULT_THRESHOLD; // is approx Color.WHITE
}
};
+ private static final BitmapVerifier GREY_ONLY_VERIFIER
+ = new ColorVerifier(Color.argb(255, 127, 127, 127),
+ PerPixelBitmapVerifier.DEFAULT_THRESHOLD);
+ private static final BitmapVerifier GREY_PARTIAL_VERIFIER
+ = new ColorVerifier(Color.argb(255, 127, 127, 127),
+ 300, 0.8f); // content will be mostly grey, for a wide range of grey
+
private static Bitmap createGridBitmap(int width, int height) {
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
for (int i = 0 ; i < width ; i++) {
for (int j = 0 ; j < height ; j++) {
- bitmap.setPixel(i, j, ((i + j * width)) % 2 == 0 ?
- Color.WHITE : Color.BLACK);
+ boolean isWhite = (i + j * width) % 2 == 0;
+ bitmap.setPixel(i, j, isWhite ? Color.WHITE : Color.BLACK);
}
}
return bitmap;
}
private static final int SMALL_GRID_SIZE = 5;
- private static Bitmap mSmallGridBitmap = createGridBitmap(SMALL_GRID_SIZE, SMALL_GRID_SIZE);
+ private Bitmap mSmallGridBitmap = createGridBitmap(SMALL_GRID_SIZE, SMALL_GRID_SIZE);
private static final int BIG_GRID_SIZE = 360;
- private static Bitmap mBigGridBitmap = createGridBitmap(BIG_GRID_SIZE, BIG_GRID_SIZE);
- private static final int HALFWAY_COLOR = Color.argb(255, 127, 127, 127);
+ private Bitmap mBigGridBitmap = createGridBitmap(BIG_GRID_SIZE, BIG_GRID_SIZE);
- /* All of these tests seem to be broken.
- * TODO: fix in L MR1
@SmallTest
public void testPaintFilterScaleUp() {
- runScaleTest(FilterEnum.PAINT_FILTER, true, mBlackWhiteVerifier);
+ runScaleTest(FilterEnum.PAINT_FILTER, true);
}
@SmallTest
public void testPaintFilterScaleDown() {
- runScaleTest(FilterEnum.PAINT_FILTER, false, new ColorVerifier(HALFWAY_COLOR, 15));
+ runScaleTest(FilterEnum.PAINT_FILTER, false);
}
@SmallTest
public void testDrawFilterRemoveFilterScaleUp() {
- runScaleTest(FilterEnum.REMOVE_FILTER, true, mBlackWhiteVerifier);
+ runScaleTest(FilterEnum.REMOVE_FILTER, true);
}
@SmallTest
public void testDrawFilterRemoveFilterScaleDown() {
- runScaleTest(FilterEnum.REMOVE_FILTER, false, mBlackWhiteVerifier);
+ runScaleTest(FilterEnum.REMOVE_FILTER, false);
}
@SmallTest
public void testDrawFilterScaleUp() {
- runScaleTest(FilterEnum.ADD_FILTER, true, mBlackWhiteVerifier);
+ runScaleTest(FilterEnum.ADD_FILTER, true);
}
@SmallTest
public void testDrawFilterScaleDown() {
- runScaleTest(FilterEnum.ADD_FILTER, false, new ColorVerifier(HALFWAY_COLOR));
+ runScaleTest(FilterEnum.ADD_FILTER, false);
}
-*/
- private void runScaleTest(final FilterEnum filterEnum, final boolean scaleUp,
- BitmapVerifier bitmapVerifier) {
+
+ private void runScaleTest(final FilterEnum filterEnum, final boolean scaleUp) {
final int gridWidth = scaleUp ? SMALL_GRID_SIZE : BIG_GRID_SIZE;
final Paint paint = new Paint(filterEnum.equals(FilterEnum.ADD_FILTER) ?
0 : Paint.FILTER_BITMAP_FLAG);
+
CanvasClient canvasClient = new CanvasClient() {
@Override
public void draw(Canvas canvas, int width, int height) {
@@ -120,6 +121,16 @@
};
createTest()
.addCanvasClient(canvasClient)
- .runWithVerifier(bitmapVerifier);
+ .runWithVerifier(getVerifierForTest(filterEnum, scaleUp));
+ }
+
+ private static BitmapVerifier getVerifierForTest(FilterEnum filterEnum, boolean scaleUp) {
+ if (filterEnum.equals(FilterEnum.REMOVE_FILTER)) {
+ // filtering disabled, so only black and white pixels will come through
+ return BLACK_WHITE_ONLY_VERIFIER;
+ }
+ // if scaling up, output pixels may have single source to sample from,
+ // will only be *mostly* grey.
+ return scaleUp ? GREY_PARTIAL_VERIFIER : GREY_ONLY_VERIFIER;
}
}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java
index afbad65..29755d8 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java
@@ -36,7 +36,7 @@
@SmallTest
public void testBlueRect() {
- final Rect rect = new Rect(10, 10, 100, 100);
+ final Rect rect = new Rect(10, 10, 80, 80);
createTest()
.addCanvasClient(new CanvasClient() {
@Override
@@ -58,8 +58,6 @@
public void draw(Canvas canvas, int width, int height) {
Paint p = new Paint();
p.setAntiAlias(false);
- p.setColor(Color.WHITE);
- canvas.drawRect(0, 0, 100, 100, p);
p.setStrokeWidth(1f);
p.setColor(Color.BLACK);
for (int i = 0; i < 10; i++) {
@@ -81,8 +79,8 @@
canvas.drawRect(0, 0, ActivityTestBase.TEST_WIDTH,
ActivityTestBase.TEST_HEIGHT, p);
p.setColor(Color.BLACK);
- p.setStrokeWidth(10);
- canvas.drawRect(10, 10, 20, 20, p);
+ p.setStrokeWidth(5);
+ canvas.drawRect(10, 10, 80, 80, p);
}
})
.runWithComparer(mExactComparer);
@@ -94,10 +92,8 @@
.addCanvasClient(new CanvasClient() {
@Override
public void draw(Canvas canvas, int width, int height) {
+ canvas.drawColor(Color.GREEN);
Paint p = new Paint();
- p.setColor(Color.GREEN);
- canvas.drawRect(0, 0, ActivityTestBase.TEST_WIDTH,
- ActivityTestBase.TEST_HEIGHT, p);
p.setColor(Color.BLACK);
p.setStrokeWidth(10);
canvas.drawLine(0, 0, 50, 0, p);
@@ -131,7 +127,7 @@
canvas.drawColor(Color.WHITE);
p.setColor(Color.BLACK);
float[] pts = {
- 0, 0, 100, 100, 100, 0, 0, 100, 50, 50, 75, 75
+ 0, 0, 80, 80, 80, 0, 0, 80, 40, 50, 60, 50
};
canvas.drawLines(pts, p);
}
@@ -185,10 +181,10 @@
public void testBluePaddedSquare() {
final NinePatchDrawable ninePatchDrawable = (NinePatchDrawable)
getActivity().getResources().getDrawable(R.drawable.blue_padded_square);
- ninePatchDrawable.setBounds(0, 0, 100, 100);
+ ninePatchDrawable.setBounds(0, 0, 90, 90);
BitmapVerifier verifier = new RectVerifier(Color.WHITE, Color.BLUE,
- new Rect(10, 10, 90, 90));
+ new Rect(10, 10, 80, 80));
createTest()
.addCanvasClient(new CanvasClient() {
@@ -197,7 +193,7 @@
canvas.drawColor(Color.WHITE);
Paint p = new Paint();
p.setColor(Color.BLUE);
- canvas.drawRect(10, 10, 90, 90, p);
+ canvas.drawRect(10, 10, 80, 80, p);
}
})
.addCanvasClient(new CanvasClient() {
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/InfrastructureTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/InfrastructureTests.java
index 6662226..c17e106 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/InfrastructureTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/InfrastructureTests.java
@@ -17,7 +17,6 @@
import com.android.cts.uirendering.R;
-import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Rect;
@@ -29,7 +28,6 @@
import android.uirendering.cts.testinfrastructure.ActivityTestBase;
import android.uirendering.cts.testinfrastructure.CanvasClient;
import android.uirendering.cts.testinfrastructure.ViewInitializer;
-import android.util.Log;
import android.view.View;
public class InfrastructureTests extends ActivityTestBase {
@@ -52,16 +50,17 @@
CanvasClient canvasClient = new CanvasClient() {
@Override
public void draw(Canvas canvas, int width, int height) {
- canvas.drawColor(canvas.isHardwareAccelerated() ? Color.WHITE : Color.BLACK);
+ canvas.drawColor(canvas.isHardwareAccelerated() ? Color.BLACK : Color.WHITE);
}
};
- // This is considered a very high threshold and as such, the test should still fail because
- // they are completely different images.
- final float threshold = 0.1f;
BitmapComparer inverseComparer = new BitmapComparer() {
@Override
public boolean verifySame(int[] ideal, int[] given, int offset, int stride, int width,
int height) {
+
+ // Return true if the images aren't even 10% similar. They should be completely
+ // different, since they should both be completely different colors.
+ final float threshold = 0.1f;
return !(new MSSIMComparer(threshold)).verifySame(ideal, given, offset, stride,
width, height);
}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayoutTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayoutTests.java
index 4667ee9..ff1e9db 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayoutTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayoutTests.java
@@ -34,7 +34,7 @@
@SmallTest
public void testSimpleRectLayout() {
createTest().addLayout(R.layout.simple_rect_layout, null, false).runWithVerifier(
- new RectVerifier(Color.WHITE, Color.BLUE, new Rect(0, 0, 100, 100)));
+ new RectVerifier(Color.WHITE, Color.BLUE, new Rect(5, 5, 85, 85)));
}
}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java
index 8df8057..6911cf0 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java
@@ -17,7 +17,7 @@
import com.android.cts.uirendering.R;
public class PathClippingTests extends ActivityTestBase {
- // draw circle with whole in it, with stroked circle
+ // draw circle with hole in it, with stroked circle
static final CanvasClient sCircleDrawCanvasClient = new CanvasClient() {
@Override
public String getDebugString() {
@@ -31,11 +31,11 @@
paint.setColor(Color.BLUE);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(20);
- canvas.drawCircle(50, 50, 40, paint);
+ canvas.drawCircle(30, 30, 40, paint);
}
};
- // draw circle with whole in it, by path operations + path clipping
+ // draw circle with hole in it, by path operations + path clipping
static final CanvasClient sCircleClipCanvasClient = new CanvasClient() {
@Override
public String getDebugString() {
@@ -47,8 +47,8 @@
canvas.save();
Path path = new Path();
- path.addCircle(50, 50, 50, Path.Direction.CW);
- path.addCircle(50, 50, 30, Path.Direction.CCW);
+ path.addCircle(30, 30, 50, Path.Direction.CW);
+ path.addCircle(30, 30, 30, Path.Direction.CCW);
canvas.clipPath(path);
canvas.drawColor(Color.BLUE);
@@ -72,12 +72,12 @@
.runWithVerifier(new SamplePointVerifier(
new Point[] {
// inside of circle
- new Point(50, 50),
+ new Point(30, 50),
// on circle
- new Point(50 + 32, 50 + 32),
+ new Point(30 + 32, 30 + 32),
// outside of circle
- new Point(50 + 38, 50 + 38),
- new Point(100, 100)
+ new Point(30 + 38, 30 + 38),
+ new Point(80, 80)
},
new int[] {
Color.WHITE,
@@ -96,8 +96,8 @@
ViewGroup rootView = (ViewGroup) view;
rootView.setClipChildren(true);
View childView = rootView.getChildAt(0);
- childView.setPivotX(50);
- childView.setPivotY(50);
+ childView.setPivotX(40);
+ childView.setPivotY(40);
childView.setRotation(45f);
}
@@ -105,11 +105,11 @@
.runWithVerifier(new SamplePointVerifier(
new Point[] {
// inside of rotated rect
- new Point(50, 50),
- new Point(50 + 32, 50 + 32),
+ new Point(40, 40),
+ new Point(40 + 25, 40 + 25),
// outside of rotated rect
- new Point(50 + 38, 50 + 38),
- new Point(100, 100)
+ new Point(40 + 31, 40 + 31),
+ new Point(80, 80)
},
new int[] {
Color.BLUE,
@@ -128,15 +128,15 @@
canvas.save();
Path path = new Path();
- path.addCircle(0, 50, 50, Path.Direction.CW);
- path.addCircle(100, 50, 50, Path.Direction.CW);
+ path.addCircle(0, 45, 45, Path.Direction.CW);
+ path.addCircle(90, 45, 45, Path.Direction.CW);
canvas.clipPath(path);
Paint paint = new Paint();
paint.setAntiAlias(true);
- paint.setTextSize(100);
+ paint.setTextSize(90);
paint.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));
- canvas.drawText("STRING", 0, 100, paint);
+ canvas.drawText("STRING", 0, 90, paint);
canvas.restore();
}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/SweepTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/SweepTests.java
index 7947286..32ab0e4 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/SweepTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/SweepTests.java
@@ -22,6 +22,7 @@
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
@@ -50,17 +51,11 @@
public static final int MULTIPLY_COLOR = 0xFF668844;
public static final int SCREEN_COLOR = 0xFFFFEEFF;
- public static final int FILTER_COLOR = 0xFFBB0000;
- public static final int RECT0_COLOR = 0x33808080;
- public static final int RECT1_COLOR = 0x66808080;
- public static final int RECT2_COLOR = 0x99808080;
- public static final int RECT3_COLOR = 0xCC808080;
-
// These points are in pairs, the first being the lower left corner, the second is only in the
// Destination bitmap, the third is the intersection of the two bitmaps, and the fourth is in
// the Source bitmap.
private final static Point[] XFERMODE_TEST_POINTS = new Point[] {
- new Point(1, 160), new Point(50, 50), new Point(70, 70), new Point(140, 140)
+ new Point(1, 80), new Point(25, 25), new Point(35, 35), new Point(70, 70)
};
/**
@@ -128,8 +123,8 @@
};
private final static DisplayModifier XFERMODE_MODIFIER = new DisplayModifier() {
- private final RectF mSrcRect = new RectF(60, 60, 160, 160);
- private final RectF mDstRect = new RectF(20, 20, 120, 120);
+ private final RectF mSrcRect = new RectF(30, 30, 80, 80);
+ private final RectF mDstRect = new RectF(10, 10, 60, 60);
private final Bitmap mSrcBitmap = createSrc();
private final Bitmap mDstBitmap = createDst();
@@ -144,8 +139,7 @@
}
private Bitmap createSrc() {
- Bitmap srcB = Bitmap.createBitmap(MODIFIER_WIDTH, MODIFIER_HEIGHT,
- Bitmap.Config.ARGB_8888);
+ Bitmap srcB = Bitmap.createBitmap(TEST_WIDTH, TEST_HEIGHT, Bitmap.Config.ARGB_8888);
Canvas srcCanvas = new Canvas(srcB);
Paint srcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
srcPaint.setColor(SRC_COLOR);
@@ -154,8 +148,7 @@
}
private Bitmap createDst() {
- Bitmap dstB = Bitmap.createBitmap(MODIFIER_WIDTH, MODIFIER_HEIGHT,
- Bitmap.Config.ARGB_8888);
+ Bitmap dstB = Bitmap.createBitmap(TEST_WIDTH, TEST_HEIGHT, Bitmap.Config.ARGB_8888);
Canvas dstCanvas = new Canvas(dstB);
Paint dstPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
dstPaint.setColor(DST_COLOR);
@@ -164,18 +157,22 @@
}
};
-
// We care about one point in each of the four rectangles of different alpha values, as well as
// the area outside the rectangles
private final static Point[] COLOR_FILTER_ALPHA_POINTS = new Point[] {
- new Point(15, 90), new Point(45, 90), new Point(75, 90), new Point(105, 90),
- new Point(135, 90)
+ new Point(9, 45),
+ new Point(27, 45),
+ new Point(45, 45),
+ new Point(63, 45),
+ new Point(81, 45)
};
- private final Map<PorterDuff.Mode, int[]> COLOR_FILTER_ALPHA_MAP = new LinkedHashMap<PorterDuff.Mode, int[]>() {
+ public static final int FILTER_COLOR = 0xFFBB0000;
+ private final Map<PorterDuff.Mode, int[]> COLOR_FILTER_ALPHA_MAP
+ = new LinkedHashMap<PorterDuff.Mode, int[]>() {
{
put(PorterDuff.Mode.SRC, new int[] {
- FILTER_COLOR, FILTER_COLOR, FILTER_COLOR, FILTER_COLOR, FILTER_COLOR
+ FILTER_COLOR, FILTER_COLOR, FILTER_COLOR, FILTER_COLOR, FILTER_COLOR
});
put(PorterDuff.Mode.DST, new int[] {
@@ -228,10 +225,17 @@
}
};
+ /**
+ * Draws 5 blocks of different color/opacity to be blended against
+ */
private final static DisplayModifier COLOR_FILTER_ALPHA_MODIFIER = new DisplayModifier() {
- private final static int mBlockWidths = 30;
- private final int[] mColorValues = new int[] {RECT0_COLOR, RECT1_COLOR, RECT2_COLOR,
- RECT3_COLOR};
+ private final int[] BLOCK_COLORS = new int[] {
+ 0x33808080,
+ 0x66808080,
+ 0x99808080,
+ 0xCC808080,
+ 0x00000000
+ };
private final Bitmap mBitmap = createQuadRectBitmap();
@@ -240,13 +244,15 @@
}
private Bitmap createQuadRectBitmap() {
- Bitmap bitmap = Bitmap.createBitmap(MODIFIER_WIDTH, MODIFIER_HEIGHT,
- Bitmap.Config.ARGB_8888);
+ Bitmap bitmap = Bitmap.createBitmap(TEST_WIDTH, TEST_HEIGHT, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
- for (int i = 0 ; i < 4 ; i++) {
- paint.setColor(mColorValues[i]);
- canvas.drawRect(i * mBlockWidths, 0, (i + 1) * mBlockWidths, MODIFIER_HEIGHT, paint);
+ paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
+ final int blockCount = BLOCK_COLORS.length;
+ final int blockWidth = TEST_WIDTH / blockCount;
+ for (int i = 0 ; i < blockCount; i++) {
+ paint.setColor(BLOCK_COLORS[i]);
+ canvas.drawRect(i * blockWidth, 0, (i + 1) * blockWidth, TEST_HEIGHT, paint);
}
return bitmap;
}
@@ -266,10 +272,9 @@
}
private Bitmap createGradient() {
- LinearGradient gradient = new LinearGradient(30, 90, 150, 90, mColors, null,
+ LinearGradient gradient = new LinearGradient(15, 45, 75, 45, mColors, null,
Shader.TileMode.REPEAT);
- Bitmap bitmap = Bitmap.createBitmap(MODIFIER_WIDTH, MODIFIER_HEIGHT,
- Bitmap.Config.ARGB_8888);
+ Bitmap bitmap = Bitmap.createBitmap(TEST_WIDTH, TEST_HEIGHT, Bitmap.Config.ARGB_8888);
Paint p = new Paint();
p.setShader(gradient);
Canvas c = new Canvas(bitmap);
@@ -281,9 +286,7 @@
public static final DisplayModifier mCircleDrawModifier = new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.drawCircle(ActivityTestBase.TEST_WIDTH / 2,
- ActivityTestBase.TEST_HEIGHT / 2,
- ActivityTestBase.TEST_HEIGHT / 2, paint);
+ canvas.drawCircle(TEST_WIDTH / 2, TEST_HEIGHT / 2, TEST_HEIGHT / 2, paint);
}
};
@@ -343,16 +346,15 @@
}
/*
- * TODO: fix this test for L MR1
@SmallTest
public void testShaderSweeps() {
- int mask = DisplayModifier.Accessor.AA_MASK |
- DisplayModifier.Accessor.SHADER_MASK |
- DisplayModifier.Accessor.XFERMODE_MASK |
- DisplayModifier.Accessor.SHAPES_MASK;
+ int mask = DisplayModifier.Accessor.AA_MASK
+ | DisplayModifier.Accessor.SHADER_MASK
+ | DisplayModifier.Accessor.XFERMODE_MASK
+ | DisplayModifier.Accessor.SHAPES_MASK;
sweepModifiersForMask(mask, null, DEFAULT_MSSIM_COMPARER, null);
}
- */
+ */
protected void sweepModifiersForMask(int mask, final DisplayModifier drawOp,
BitmapComparer[] bitmapComparers, BitmapVerifier[] bitmapVerifiers) {
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewClippingTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewClippingTests.java
index da2db48..343228f 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewClippingTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewClippingTests.java
@@ -20,11 +20,11 @@
* Since the layout is blue on a white background, this is always done with a RectVerifier.
*/
public class ViewClippingTests extends ActivityTestBase {
- final Rect FULL_RECT = new Rect(0, 0, 200, 200);
- final Rect BOUNDS_RECT = new Rect(0, 0, 100, 100);
- final Rect PADDED_RECT = new Rect(15, 16, 83, 82);
- final Rect OUTLINE_RECT = new Rect(1, 2, 98, 99);
- final Rect CLIP_BOUNDS_RECT = new Rect(10, 20, 70, 80);
+ final Rect FULL_RECT = new Rect(0, 0, 90, 90);
+ final Rect BOUNDS_RECT = new Rect(0, 0, 80, 80);
+ final Rect PADDED_RECT = new Rect(15, 16, 63, 62);
+ final Rect OUTLINE_RECT = new Rect(1, 2, 78, 79);
+ final Rect CLIP_BOUNDS_RECT = new Rect(10, 20, 50, 60);
final ViewInitializer BOUNDS_CLIP_INIT = new ViewInitializer() {
@Override
@@ -61,10 +61,9 @@
}
};
- // TODO: attempt to reduce
- static final int TOLERANCE = 16;
static BitmapVerifier makeClipVerifier(Rect blueBoundsRect) {
- return new RectVerifier(Color.WHITE, Color.BLUE, blueBoundsRect, TOLERANCE);
+ // very high error tolerance, since all these tests care about is clip alignment
+ return new RectVerifier(Color.WHITE, Color.BLUE, blueBoundsRect, 75);
}
public void testSimpleUnclipped() {
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
index 386c015..e3ad5a6 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
@@ -41,15 +41,16 @@
public static final String TAG = "ActivityTestBase";
public static final boolean DEBUG = false;
public static final boolean USE_RS = false;
- public static final int TEST_WIDTH = 180;
- public static final int TEST_HEIGHT = 180; //The minimum height and width of a device
+
+ //The minimum height and width of a device
+ public static final int TEST_WIDTH = 90;
+ public static final int TEST_HEIGHT = 90;
+
public static final int MAX_SCREEN_SHOTS = 100;
private int[] mHardwareArray = new int[TEST_HEIGHT * TEST_WIDTH];
private int[] mSoftwareArray = new int[TEST_HEIGHT * TEST_WIDTH];
private DifferenceVisualizer mDifferenceVisualizer;
- private Allocation mIdealAllocation;
- private Allocation mGivenAllocation;
private RenderScript mRenderScript;
private TestCaseBuilder mTestCaseBuilder;
@@ -122,16 +123,24 @@
return pixels;
}
+ private Bitmap takeScreenshotImpl() {
+ Bitmap source = getInstrumentation().getUiAutomation().takeScreenshot();
+ int x = (source.getWidth() - TEST_WIDTH) / 2;
+ int y = (source.getHeight() - TEST_HEIGHT) / 2;
+ return Bitmap.createBitmap(source, x, y, TEST_WIDTH, TEST_HEIGHT);
+ }
+
public Bitmap takeScreenshot() {
getInstrumentation().waitForIdleSync();
- Bitmap bitmap1 = getInstrumentation().getUiAutomation().takeScreenshot();
+ Bitmap bitmap1 = takeScreenshotImpl();
Bitmap bitmap2;
int count = 0;
do {
bitmap2 = bitmap1;
- bitmap1 = getInstrumentation().getUiAutomation().takeScreenshot();
+ bitmap1 = takeScreenshotImpl();
count++;
- } while (count < MAX_SCREEN_SHOTS && !Arrays.equals(getBitmapPixels(bitmap2), getBitmapPixels(bitmap1)));
+ } while (count < MAX_SCREEN_SHOTS &&
+ !Arrays.equals(getBitmapPixels(bitmap2), getBitmapPixels(bitmap1)));
return bitmap1;
}
@@ -161,12 +170,12 @@
boolean success;
if (USE_RS && comparer.supportsRenderScript()) {
- mIdealAllocation = Allocation.createFromBitmap(mRenderScript, bitmap1,
+ Allocation idealAllocation = Allocation.createFromBitmap(mRenderScript, bitmap1,
Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
- mGivenAllocation = Allocation.createFromBitmap(mRenderScript, bitmap2,
+ Allocation givenAllocation = Allocation.createFromBitmap(mRenderScript, bitmap2,
Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
- success = comparer.verifySameRS(getActivity().getResources(), mIdealAllocation,
- mGivenAllocation, 0, TEST_WIDTH, TEST_WIDTH, TEST_HEIGHT, mRenderScript);
+ success = comparer.verifySameRS(getActivity().getResources(), idealAllocation,
+ givenAllocation, 0, TEST_WIDTH, TEST_WIDTH, TEST_HEIGHT, mRenderScript);
} else {
bitmap1.getPixels(mSoftwareArray, 0, TEST_WIDTH, 0, 0, TEST_WIDTH, TEST_HEIGHT);
bitmap2.getPixels(mHardwareArray, 0, TEST_WIDTH, 0, 0, TEST_WIDTH, TEST_HEIGHT);
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/CanvasClientView.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/CanvasClientView.java
index 92242f0..60127ae 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/CanvasClientView.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/CanvasClientView.java
@@ -19,6 +19,7 @@
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
+import android.util.AttributeSet;
import android.view.View;
/**
@@ -26,14 +27,21 @@
*/
public class CanvasClientView extends View {
private CanvasClient mCanvasClient;
- private int mWidth;
- private int mHeight;
- public CanvasClientView(Context context, CanvasClient canvasClient, int width, int height) {
+ public CanvasClientView(Context context) {
super(context);
+ }
+
+ public CanvasClientView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public CanvasClientView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public void setCanvasClient(CanvasClient canvasClient) {
mCanvasClient = canvasClient;
- mWidth = width;
- mHeight = height;
}
@Override
@@ -45,11 +53,11 @@
paint.setTextSize(20);
canvas.drawText(s, 200, 200, paint);
}
- if (mCanvasClient != null) {
- canvas.save();
- canvas.clipRect(0, 0, mWidth, mHeight);
- mCanvasClient.draw(canvas, mWidth, mHeight);
- canvas.restore();
- }
+ if (mCanvasClient == null) throw new IllegalStateException("Canvas client missing");
+
+ canvas.save();
+ canvas.clipRect(0, 0, ActivityTestBase.TEST_WIDTH, ActivityTestBase.TEST_HEIGHT);
+ mCanvasClient.draw(canvas, ActivityTestBase.TEST_WIDTH, ActivityTestBase.TEST_HEIGHT);
+ canvas.restore();
}
}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DisplayModifier.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DisplayModifier.java
index 684293d..b42ac88 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DisplayModifier.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DisplayModifier.java
@@ -32,17 +32,16 @@
* Modifies the canvas and paint objects when called.
*/
public abstract class DisplayModifier {
- private static final RectF gRect = new RectF(0, 0, 100, 100);
- private static final float[] gPts = new float[]{
- 0, 100, 100, 0, 100, 200, 200, 100
+ private static final RectF RECT = new RectF(0, 0, 100, 100);
+ private static final float[] POINTS = new float[]{
+ 0, 40, 40, 0, 40, 80, 80, 40
};
- private static final float[] gTriPts = new float[]{
- 75, 0, 130, 130, 130, 130, 0, 130, 0, 130, 75, 0
+ private static final float[] TRIANGLE_POINTS = new float[]{
+ 40, 0, 80, 80, 80, 80, 0, 80, 0, 80, 40, 0
};
- private static final int NUM_PARALLEL_LINES = 24;
- private static final float[] gLinePts = new float[NUM_PARALLEL_LINES * 8 + gTriPts.length];
- protected static final int MODIFIER_WIDTH = 180;
- protected static final int MODIFIER_HEIGHT = 180;
+ private static final int NUM_PARALLEL_LINES = 10;
+ private static final float[] LINES = new float[NUM_PARALLEL_LINES * 8
+ + TRIANGLE_POINTS.length];
public static final PorterDuff.Mode[] PORTERDUFF_MODES = new PorterDuff.Mode[] {
PorterDuff.Mode.SRC, PorterDuff.Mode.DST, PorterDuff.Mode.SRC_OVER,
@@ -53,25 +52,23 @@
};
static {
- int index;
- for (index = 0; index < gTriPts.length; index++) {
- gLinePts[index] = gTriPts[index];
- }
+ System.arraycopy(TRIANGLE_POINTS, 0, LINES, 0, TRIANGLE_POINTS.length);
+ int index = TRIANGLE_POINTS.length;
float val = 0;
for (int i = 0; i < NUM_PARALLEL_LINES; i++) {
- gLinePts[index + 0] = 150;
- gLinePts[index + 1] = val;
- gLinePts[index + 2] = 300;
- gLinePts[index + 3] = val;
+ LINES[index + 0] = 40;
+ LINES[index + 1] = val;
+ LINES[index + 2] = 80;
+ LINES[index + 3] = val;
index += 4;
val += 8 + (2.0f / NUM_PARALLEL_LINES);
}
val = 0;
for (int i = 0; i < NUM_PARALLEL_LINES; i++) {
- gLinePts[index + 0] = val;
- gLinePts[index + 1] = 150;
- gLinePts[index + 2] = val;
- gLinePts[index + 3] = 300;
+ LINES[index + 0] = val;
+ LINES[index + 1] = 40;
+ LINES[index + 2] = val;
+ LINES[index + 3] = 80;
index += 4;
val += 8 + (2.0f / NUM_PARALLEL_LINES);
}
@@ -81,7 +78,7 @@
// paint object, like anti-aliasing or drawing. Within those LinkedHashMaps are the various
// options for that specific topic, which contains a displaymodifier which will affect the
// given canvas and paint objects.
- public static final LinkedHashMap<String, LinkedHashMap<String, DisplayModifier>> sMaps =
+ public static final LinkedHashMap<String, LinkedHashMap<String, DisplayModifier>> MAPS =
new LinkedHashMap<String, LinkedHashMap<String,DisplayModifier>>() {
{
put("aa", new LinkedHashMap<String, DisplayModifier>() {
@@ -225,7 +222,7 @@
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
canvas.rotate(90);
- canvas.translate(0, -200);
+ canvas.translate(0, -100);
}
});
put("scale2x2", new DisplayModifier() {
@@ -347,13 +344,13 @@
put("roundRect", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.drawRoundRect(gRect, 20, 20, paint);
+ canvas.drawRoundRect(RECT, 20, 20, paint);
}
});
put("rect", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.drawRect(gRect, paint);
+ canvas.drawRect(RECT, paint);
}
});
put("circle", new DisplayModifier() {
@@ -365,36 +362,32 @@
put("oval", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.drawOval(gRect, paint);
+ canvas.drawOval(RECT, paint);
}
});
put("lines", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.drawLines(gLinePts, paint);
+ canvas.drawLines(LINES, paint);
}
});
- /* drawPoints does not work with zero stroke width,
- * but it isn't a regression
- * TODO: fix hardware canvas so that drawPoints works
put("plusPoints", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.drawPoints(gPts, paint);
+ canvas.drawPoints(POINTS, paint);
}
});
- */
put("text", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setTextSize(36);
+ paint.setTextSize(20);
canvas.drawText("TEXTTEST", 0, 50, paint);
}
});
put("shadowtext", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setTextSize(36);
+ paint.setTextSize(20);
paint.setShadowLayer(3.0f, 0.0f, 3.0f, 0xffff00ff);
canvas.drawText("TEXTTEST", 0, 50, paint);
}
@@ -410,13 +403,13 @@
put("arc", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.drawArc(gRect, 260, 285, false, paint);
+ canvas.drawArc(RECT, 260, 285, false, paint);
}
});
put("arcFromCenter", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.drawArc(gRect, 260, 285, true, paint);
+ canvas.drawArc(RECT, 260, 285, true, paint);
}
});
}
@@ -454,9 +447,9 @@
// Create a Display Map of the valid indices
mDisplayMap = new LinkedHashMap<String, LinkedHashMap<String, DisplayModifier>>();
int index = 0;
- for (String key : DisplayModifier.sMaps.keySet()) {
+ for (String key : DisplayModifier.MAPS.keySet()) {
if (validIndex(index)) {
- mDisplayMap.put(key, DisplayModifier.sMaps.get(key));
+ mDisplayMap.put(key, DisplayModifier.MAPS.get(key));
}
index++;
}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
index 166b6ff..65ed7ee 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
@@ -22,6 +22,7 @@
import android.os.Handler;
import android.os.Message;
import android.view.View;
+import android.view.ViewStub;
import android.view.ViewTreeObserver;
import android.webkit.WebView;
@@ -41,6 +42,8 @@
public void onCreate(Bundle bundle){
super.onCreate(bundle);
+ getWindow().getDecorView().setSystemUiVisibility(
+ View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN);
mHandler = new RenderSpecHandler();
int uiMode = getResources().getConfiguration().uiMode;
mOnWatch = (uiMode & Configuration.UI_MODE_TYPE_WATCH) == Configuration.UI_MODE_TYPE_WATCH;
@@ -83,39 +86,40 @@
}
public void handleMessage(Message message) {
- int webViewBuffer = 0;
+ int drawCountDelay = 0;
+ setContentView(R.layout.test_container);
+ ViewStub stub = (ViewStub) findViewById(R.id.test_content_stub);
switch (message.what) {
case LAYOUT_MSG: {
- setContentView(message.arg1);
- mView = findViewById(R.id.test_root);
- if (mView == null) {
- throw new IllegalStateException("test_root failed to inflate");
- }
+ stub.setLayoutResource(message.arg1);
+ mView = stub.inflate();
} break;
case CANVAS_MSG: {
- mView = new CanvasClientView(getApplicationContext(),
- (CanvasClient) (message.obj), ActivityTestBase.TEST_WIDTH,
- ActivityTestBase.TEST_HEIGHT);
- setContentView(mView);
+ stub.setLayoutResource(R.layout.test_content_canvasclientview);
+ mView = stub.inflate();
+ ((CanvasClientView) mView).setCanvasClient((CanvasClient) (message.obj));
} break;
case WEB_VIEW_MSG: {
- mView = new WebView(getApplicationContext());
+ stub.setLayoutResource(R.layout.test_content_webview);
+ mView = stub.inflate();
((WebView) mView).loadUrl((String) message.obj);
((WebView) mView).setInitialScale(100);
- setContentView(mView);
- webViewBuffer = 10;
+ drawCountDelay = 10;
} break;
}
+ if (mView == null) {
+ throw new IllegalStateException("failed to inflate test content");
+ }
+
if (mViewInitializer != null) {
mViewInitializer.intializeView(mView);
}
-
mView.setLayerType(message.arg2, null);
- DrawCounterListener onDrawListener = new DrawCounterListener(webViewBuffer);
+ DrawCounterListener onDrawListener = new DrawCounterListener(drawCountDelay);
mView.getViewTreeObserver().addOnPreDrawListener(onDrawListener);
diff --git a/tests/tests/view/src/android/view/cts/MenuInflaterTest.java b/tests/tests/view/src/android/view/cts/MenuInflaterTest.java
index 40d1d3d..6007730 100644
--- a/tests/tests/view/src/android/view/cts/MenuInflaterTest.java
+++ b/tests/tests/view/src/android/view/cts/MenuInflaterTest.java
@@ -19,7 +19,6 @@
import com.android.cts.view.R;
import com.android.internal.view.menu.MenuBuilder;
-
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
@@ -28,6 +27,7 @@
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.test.ActivityInstrumentationTestCase2;
+import android.test.UiThreadTest;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.SubMenu;
@@ -48,17 +48,22 @@
protected void setUp() throws Exception {
super.setUp();
mActivity = getActivity();
- mMenuInflater = mActivity.getMenuInflater();
}
+ @UiThreadTest
public void testConstructor() {
new MenuInflater(mActivity);
}
+ @UiThreadTest
public void testInflate() {
Menu menu = new MenuBuilder(mActivity);
assertEquals(0, menu.size());
+ if (mMenuInflater == null) {
+ mMenuInflater = mActivity.getMenuInflater();
+ }
+
mMenuInflater.inflate(com.android.cts.view.R.menu.browser, menu);
assertNotNull(menu);
assertEquals(1, menu.size());
@@ -77,7 +82,12 @@
}
// Check wheher the objects are created correctly from xml files
+ @UiThreadTest
public void testInflateFromXml(){
+ if (mMenuInflater == null) {
+ mMenuInflater = mActivity.getMenuInflater();
+ }
+
// the visibility and shortcut
Menu menu = new MenuBuilder(mActivity);
mMenuInflater.inflate(R.menu.visible_shortcut, menu);
diff --git a/tests/tests/widget/src/android/widget/cts/TextViewTest.java b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
index 24b8fdb..480e1a6 100644
--- a/tests/tests/widget/src/android/widget/cts/TextViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
@@ -2845,15 +2845,19 @@
assertEquals(1, mTextView.getImeActionId());
}
- @UiThreadTest
public void testSetTextLong() {
- final int MAX_COUNT = 1 << 21;
- char[] longText = new char[MAX_COUNT];
- for (int n = 0; n < MAX_COUNT; n++) {
- longText[n] = 'm';
- }
- mTextView = findTextView(R.id.textview_text);
- mTextView.setText(new String(longText));
+ mActivity.runOnUiThread(new Runnable() {
+ public void run() {
+ final int MAX_COUNT = 1 << 21;
+ char[] longText = new char[MAX_COUNT];
+ for (int n = 0; n < MAX_COUNT; n++) {
+ longText[n] = 'm';
+ }
+ mTextView = findTextView(R.id.textview_text);
+ mTextView.setText(new String(longText));
+ }
+ });
+ mInstrumentation.waitForIdleSync();
}
@UiThreadTest
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/PrintTestRemoteTestRunner.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/PrintTestRemoteTestRunner.java
index 3d92eb3..72dccd4 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/PrintTestRemoteTestRunner.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/PrintTestRemoteTestRunner.java
@@ -185,6 +185,11 @@
}
@Override
+ public void setTestCollection(boolean b) {
+ throw new UnsupportedOperationException("Test Collection mode is not supported");
+ }
+
+ @Override
public void setTestSize(TestSize size) {
addInstrumentationArg(SIZE_ARG_NAME, ""/*size.getRunnerValue()*/);
}