diff options
Diffstat (limited to 'tests')
86 files changed, 2419 insertions, 2159 deletions
diff --git a/tests/ApkVerityTest/AndroidTest.xml b/tests/ApkVerityTest/AndroidTest.xml index 39b75cc27acb..2a0a2c76e50f 100644 --- a/tests/ApkVerityTest/AndroidTest.xml +++ b/tests/ApkVerityTest/AndroidTest.xml @@ -43,6 +43,8 @@ <option name="push" value="block_device_writer->/data/local/tmp/block_device_writer" /> </target_preparer> + <!-- Skip on HWASan. TODO(b/232288278): Re-enable --> + <object type="module_controller" class="com.android.tradefed.testtype.suite.module.SkipHWASanModuleController" /> <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" > <option name="jar" value="ApkVerityTest.jar" /> </test> diff --git a/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java b/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java index 90ddb6ffb34a..d2a6bf288be4 100644 --- a/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java +++ b/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java @@ -31,6 +31,7 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -299,6 +300,8 @@ public final class BackgroundDexOptServiceIntegrationTests { // Test that background dexopt under low storage conditions downgrades unused packages. @Test + @Ignore("b/251438180: This test has been failing for a long time; temporarily disable it while" + + " we investigate this issue.") public void testBackgroundDexOptDowngradeSuccessful() throws IOException { // Should be more than DOWNGRADE_AFTER_DAYS. long deltaDays = DOWNGRADE_AFTER_DAYS + 1; diff --git a/tests/CanvasCompare/Android.bp b/tests/CanvasCompare/Android.bp deleted file mode 100644 index 98831154ddc2..000000000000 --- a/tests/CanvasCompare/Android.bp +++ /dev/null @@ -1,63 +0,0 @@ -// -// Copyright (C) 2012 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -package { - // See: http://go/android-license-faq - default_applicable_licenses: [ - "frameworks_base_license", - ], -} - -android_test { - name: "CanvasCompare", - srcs: [ - "src/**/*.java", - ":CanvasCompare-rscript{CanvasCompare.srcjar}", - ], - resource_zips: [ - ":CanvasCompare-rscript{CanvasCompare.res.zip}", - ], - platform_apis: true, - libs: [ - "android.test.runner", - "android.test.base", - ], - static_libs: ["junit"], -} - -genrule { - name: "CanvasCompare-rscript", - srcs: [ - "src/**/*.rscript", - ":rs_script_api", - ":rs_clang_headers", - ], - tools: [ - "llvm-rs-cc", - "soong_zip", - ], - out: [ - "CanvasCompare.srcjar", - "CanvasCompare.res.zip", - ], - cmd: "for f in $(locations src/**/*.rscript); do " + - " $(location llvm-rs-cc) -o $(genDir)/res/raw -p $(genDir)/src " + - " -I $$(dirname $$(echo $(locations :rs_script_api) | awk '{ print $$1 }')) " + - " -I $$(dirname $$(echo $(locations :rs_clang_headers) | awk '{ print $$1 }')) $${f}; " + - "done && " + - "$(location soong_zip) -srcjar -o $(location CanvasCompare.srcjar) -C $(genDir)/src -D $(genDir)/src &&" + - "$(location soong_zip) -o $(location CanvasCompare.res.zip) -C $(genDir)/res -D $(genDir)/res", -} diff --git a/tests/CanvasCompare/AndroidManifest.xml b/tests/CanvasCompare/AndroidManifest.xml deleted file mode 100644 index 2734e7f07f27..000000000000 --- a/tests/CanvasCompare/AndroidManifest.xml +++ /dev/null @@ -1,48 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2012 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.test.hwuicompare"> - - <uses-permission android:name="android.permission.INTERNET"/> - <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> - <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> - - <application android:label="@string/app_name" - android:theme="@android:style/Theme.Holo.Light.NoActionBar"> - <activity android:name="AutomaticActivity" - android:label="CanvasAutoCompare" - android:exported="true"> - <intent-filter> - <action android:name="android.intent.action.MAIN"/> - <category android:name="android.intent.category.LAUNCHER"/> - </intent-filter> - </activity> - <activity android:name="ManualActivity" - android:label="CanvasManualCompare" - android:exported="true"> - <intent-filter> - <action android:name="android.intent.action.MAIN"/> - <category android:name="android.intent.category.LAUNCHER"/> - </intent-filter> - </activity> - <uses-library android:name="android.test.runner"/> - </application> - <instrumentation android:name="android.test.InstrumentationTestRunner" - android:targetPackage="com.android.test.hwuicompare" - android:label="HW/SW Canvas comparison tool."/> - -</manifest> diff --git a/tests/CanvasCompare/OWNERS b/tests/CanvasCompare/OWNERS deleted file mode 100644 index c88a9f82c347..000000000000 --- a/tests/CanvasCompare/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /libs/hwui/OWNERS diff --git a/tests/CanvasCompare/res/drawable/sunset1.jpg b/tests/CanvasCompare/res/drawable/sunset1.jpg Binary files differdeleted file mode 100644 index 3b4e056b70d0..000000000000 --- a/tests/CanvasCompare/res/drawable/sunset1.jpg +++ /dev/null diff --git a/tests/CanvasCompare/res/layout/automatic_layout.xml b/tests/CanvasCompare/res/layout/automatic_layout.xml deleted file mode 100644 index e049ec0a5000..000000000000 --- a/tests/CanvasCompare/res/layout/automatic_layout.xml +++ /dev/null @@ -1,38 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2012 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" > - - <com.android.test.hwuicompare.MainView - android:id="@+id/hardware_view" - android:layout_width="@dimen/layer_width" - android:layout_height="@dimen/layer_width" /> - - <ImageView - android:id="@+id/software_image_view" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentRight="true" /> - - <ImageView - android:id="@+id/hardware_image_view" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentBottom="true" - android:layout_alignParentRight="true" /> - -</RelativeLayout> diff --git a/tests/CanvasCompare/res/layout/manual_layout.xml b/tests/CanvasCompare/res/layout/manual_layout.xml deleted file mode 100644 index 1a9288ce1993..000000000000 --- a/tests/CanvasCompare/res/layout/manual_layout.xml +++ /dev/null @@ -1,119 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2012 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:gravity="center_horizontal" - android:orientation="vertical" > - - <HorizontalScrollView - android:layout_width="wrap_content" - android:layout_height="wrap_content" > - - <LinearLayout - android:id="@+id/spinner_layout" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="horizontal" /> - </HorizontalScrollView> - - <LinearLayout - android:layout_width="match_parent" - android:layout_height="0dip" - android:layout_weight="1" - android:baselineAligned="true" - android:orientation="horizontal" > - - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_weight="1" - android:gravity="center" - android:orientation="horizontal" > - - <com.android.test.hwuicompare.MainView - android:id="@+id/hardware_view" - android:layout_width="@dimen/layer_width" - android:layout_height="@dimen/layer_width" /> - </LinearLayout> - - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_weight="1" - android:gravity="center" - android:orientation="horizontal" > - - <com.android.test.hwuicompare.MainView - android:id="@+id/software_view" - android:layout_width="@dimen/layer_width" - android:layout_height="@dimen/layer_width" /> - </LinearLayout> - </LinearLayout> - - <ImageView - android:id="@+id/compare_image_view" - android:layout_width="@dimen/layer_width_double" - android:layout_height="@dimen/layer_height_double" - android:filter="false" /> - - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center" - android:orientation="horizontal" > - - <ImageButton - android:id="@+id/previous" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:contentDescription="@string/previous_combination" - android:src="@android:drawable/ic_media_previous" /> - - <ImageButton - android:id="@+id/next" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:contentDescription="@string/next_combination" - android:src="@android:drawable/ic_media_next" /> - - <TextView - android:id="@+id/current_error" - android:layout_width="100dp" - android:layout_height="wrap_content" - android:gravity="center" - android:textAppearance="?android:attr/textAppearanceLarge" /> - - <Button - android:id="@+id/show_hardware_version" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/show_hardware_version" /> - - <Button - android:id="@+id/show_software_version" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/show_software_version" /> - - <Button - android:id="@+id/show_error_heatmap" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/show_error_heatmap" /> - </LinearLayout> - -</LinearLayout> diff --git a/tests/CanvasCompare/res/values/strings.xml b/tests/CanvasCompare/res/values/strings.xml deleted file mode 100644 index edd46103f4f5..000000000000 --- a/tests/CanvasCompare/res/values/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2012 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<resources> - <string name="app_name">Canvas Compare Test</string> - - <!-- show hardware rendered version of the layer --> - <string name="show_hardware_version">Hardware</string> - <!-- show software rendered version of the layer --> - <string name="show_software_version">Software</string> - <!-- show layer error --> - <string name="show_error_values">Error</string> - <!-- show layer error heatmap --> - <string name="show_error_heatmap">Heatmap</string> - <!-- select and display the next combination of painting options--> - <string name="next_combination">Next Combination</string> - <!-- select and display the previous combination of painting options--> - <string name="previous_combination">Previous Combination</string> -</resources> diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/AutomaticActivity.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/AutomaticActivity.java deleted file mode 100644 index 8ccd4e2181ed..000000000000 --- a/tests/CanvasCompare/src/com/android/test/hwuicompare/AutomaticActivity.java +++ /dev/null @@ -1,310 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.test.hwuicompare; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashMap; -import java.util.TreeSet; - -import org.json.JSONException; -import org.json.JSONObject; - -import android.os.Bundle; -import android.os.Environment; -import android.os.Trace; -import android.util.Log; -import android.widget.ImageView; -import android.widget.Toast; - -public class AutomaticActivity extends CompareActivity { - private static final String LOG_TAG = "AutomaticActivity"; - private static final float ERROR_DISPLAY_THRESHOLD = 0.01f; - protected static final boolean DRAW_BITMAPS = false; - - /** - * Threshold of error change required to consider a test regressed/improved - */ - private static final float ERROR_CHANGE_THRESHOLD = 0.001f; - - private static final float[] ERROR_CUTOFFS = { - 0, 0.005f, 0.01f, 0.02f, 0.05f, 0.1f, 0.25f, 0.5f, 1f, 2f - }; - - private final float[] mErrorRates = new float[ERROR_CUTOFFS.length]; - private float mTotalTests = 0; - private float mTotalError = 0; - private int mTestsRegressed = 0; - private int mTestsImproved = 0; - - private ImageView mSoftwareImageView = null; - private ImageView mHardwareImageView = null; - - - public abstract static class FinalCallback { - abstract void report(String name, float value); - void complete() {}; - } - - private final ArrayList<FinalCallback> mFinalCallbacks = new ArrayList<FinalCallback>(); - - Runnable mRunnable = new Runnable() { - @Override - public void run() { - loadBitmaps(); - if (mSoftwareBitmap == null || mHardwareBitmap == null) { - Log.e(LOG_TAG, "bitmap is null"); - return; - } - - if (DRAW_BITMAPS) { - mSoftwareImageView.setImageBitmap(mSoftwareBitmap); - mHardwareImageView.setImageBitmap(mHardwareBitmap); - } - - Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, "calculateError"); - float error = mErrorCalculator.calcErrorRS(mSoftwareBitmap, mHardwareBitmap); - Trace.traceEnd(Trace.TRACE_TAG_ALWAYS); - - final String[] modifierNames = DisplayModifier.getLastAppliedModifications(); - handleError(modifierNames, error); - - if (DisplayModifier.step()) { - finishTest(); - } else { - mHardwareView.invalidate(); - if (DRAW_BITMAPS) { - mSoftwareImageView.invalidate(); - mHardwareImageView.invalidate(); - } - } - mHandler.removeCallbacks(mRunnable); - } - }; - - @Override - protected void onPause() { - super.onPause(); - mHandler.removeCallbacks(mRunnable); - }; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.automatic_layout); - - mSoftwareImageView = findViewById(R.id.software_image_view); - mHardwareImageView = findViewById(R.id.hardware_image_view); - - onCreateCommon(mRunnable); - beginTest(); - } - - private static class TestResult { - TestResult(String label, float error) { - mLabel = label; - mTotalError = error; - mCount = 1; - } - public void addInto(float error) { - mTotalError += error; - mCount++; - } - public float getAverage() { - return mTotalError / mCount; - } - final String mLabel; - float mTotalError; - int mCount; - } - - JSONObject mOutputJson = null; - JSONObject mInputJson = null; - final HashMap<String, TestResult> mModifierResults = new HashMap<String, TestResult>(); - final HashMap<String, TestResult> mIndividualResults = new HashMap<String, TestResult>(); - final HashMap<String, TestResult> mModifierDiffResults = new HashMap<String, TestResult>(); - final HashMap<String, TestResult> mIndividualDiffResults = new HashMap<String, TestResult>(); - private void beginTest() { - mFinalCallbacks.add(new FinalCallback() { - @Override - void report(String name, float value) { - Log.d(LOG_TAG, name + " " + value); - }; - }); - - File inputFile = new File(Environment.getExternalStorageDirectory(), - "CanvasCompareInput.json"); - if (inputFile.exists() && inputFile.canRead() && inputFile.length() > 0) { - try { - FileInputStream inputStream = new FileInputStream(inputFile); - Log.d(LOG_TAG, "Parsing input file..."); - StringBuffer content = new StringBuffer((int)inputFile.length()); - byte[] buffer = new byte[1024]; - while (inputStream.read(buffer) != -1) { - content.append(new String(buffer)); - } - mInputJson = new JSONObject(content.toString()); - inputStream.close(); - Log.d(LOG_TAG, "Parsed input file with " + mInputJson.length() + " entries"); - } catch (JSONException e) { - Log.e(LOG_TAG, "error parsing input json", e); - } catch (IOException e) { - Log.e(LOG_TAG, "error reading input json from sd", e); - } - } - - mOutputJson = new JSONObject(); - } - - private static void logTestResultHash(String label, HashMap<String, TestResult> map) { - Log.d(LOG_TAG, "---------------"); - Log.d(LOG_TAG, label + ":"); - Log.d(LOG_TAG, "---------------"); - TreeSet<TestResult> set = new TreeSet<TestResult>(new Comparator<TestResult>() { - @Override - public int compare(TestResult lhs, TestResult rhs) { - if (lhs == rhs) return 0; // don't need to worry about complex equality - - int cmp = Float.compare(lhs.getAverage(), rhs.getAverage()); - if (cmp != 0) { - return cmp; - } - return lhs.mLabel.compareTo(rhs.mLabel); - } - }); - - for (TestResult t : map.values()) { - set.add(t); - } - - for (TestResult t : set.descendingSet()) { - if (Math.abs(t.getAverage()) > ERROR_DISPLAY_THRESHOLD) { - Log.d(LOG_TAG, String.format("%2.4f : %s", t.getAverage(), t.mLabel)); - } - } - Log.d(LOG_TAG, ""); - } - - private void finishTest() { - for (FinalCallback c : mFinalCallbacks) { - c.report("averageError", (mTotalError / mTotalTests)); - for (int i = 1; i < ERROR_CUTOFFS.length; i++) { - c.report(String.format("tests with error over %1.3f", ERROR_CUTOFFS[i]), - mErrorRates[i]); - } - if (mInputJson != null) { - c.report("tests regressed", mTestsRegressed); - c.report("tests improved", mTestsImproved); - } - c.complete(); - } - - try { - if (mOutputJson != null) { - String outputString = mOutputJson.toString(4); - File outputFile = new File(Environment.getExternalStorageDirectory(), - "CanvasCompareOutput.json"); - FileOutputStream outputStream = new FileOutputStream(outputFile); - outputStream.write(outputString.getBytes()); - outputStream.close(); - Log.d(LOG_TAG, "Saved output file with " + mOutputJson.length() + " entries"); - } - } catch (JSONException e) { - Log.e(LOG_TAG, "error during JSON stringify", e); - } catch (IOException e) { - Log.e(LOG_TAG, "error storing JSON output on sd", e); - } - - logTestResultHash("Modifier change vs previous", mModifierDiffResults); - logTestResultHash("Invidual test change vs previous", mIndividualDiffResults); - logTestResultHash("Modifier average test results", mModifierResults); - logTestResultHash("Individual test results", mIndividualResults); - - Toast.makeText(getApplicationContext(), "done!", Toast.LENGTH_SHORT).show(); - finish(); - } - - /** - * Inserts the error value into all TestResult objects, associated with each of its modifiers - */ - private static void addForAllModifiers(String fullName, float error, String[] modifierNames, - HashMap<String, TestResult> modifierResults) { - for (String modifierName : modifierNames) { - TestResult r = modifierResults.get(fullName); - if (r == null) { - modifierResults.put(modifierName, new TestResult(modifierName, error)); - } else { - r.addInto(error); - } - } - } - - private void handleError(final String[] modifierNames, final float error) { - String fullName = ""; - for (String s : modifierNames) { - fullName = fullName.concat("." + s); - } - fullName = fullName.substring(1); - - float deltaError = 0; - if (mInputJson != null) { - try { - deltaError = error - (float)mInputJson.getDouble(fullName); - } catch (JSONException e) { - Log.w(LOG_TAG, "Warning: unable to read from input json", e); - } - if (deltaError > ERROR_CHANGE_THRESHOLD) mTestsRegressed++; - if (deltaError < -ERROR_CHANGE_THRESHOLD) mTestsImproved++; - mIndividualDiffResults.put(fullName, new TestResult(fullName, deltaError)); - addForAllModifiers(fullName, deltaError, modifierNames, mModifierDiffResults); - } - - mIndividualResults.put(fullName, new TestResult(fullName, error)); - addForAllModifiers(fullName, error, modifierNames, mModifierResults); - - try { - if (mOutputJson != null) { - mOutputJson.put(fullName, error); - } - } catch (JSONException e) { - Log.e(LOG_TAG, "exception during JSON recording", e); - mOutputJson = null; - } - - for (int i = 0; i < ERROR_CUTOFFS.length; i++) { - if (error <= ERROR_CUTOFFS[i]) break; - mErrorRates[i]++; - } - mTotalError += error; - mTotalTests++; - } - - @Override - protected boolean forceRecreateBitmaps() { - // disable, unless needed for drawing into imageviews - return DRAW_BITMAPS; - } - - // FOR TESTING - public void setFinalCallback(FinalCallback c) { - mFinalCallbacks.add(c); - } -} diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/CompareActivity.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/CompareActivity.java deleted file mode 100644 index 0dec1de79a46..000000000000 --- a/tests/CanvasCompare/src/com/android/test/hwuicompare/CompareActivity.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.test.hwuicompare; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -import com.android.test.hwuicompare.R; - -import android.app.Activity; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.drawable.ColorDrawable; -import android.os.Handler; -import android.os.Trace; -import android.util.Log; -import android.view.View; - -abstract public class CompareActivity extends Activity { - private static final String LOG_TAG = "CompareActivity"; - - protected MainView mHardwareView = null; - - protected Bitmap mSoftwareBitmap; - protected Bitmap mHardwareBitmap; - - protected ErrorCalculator mErrorCalculator; - - protected Handler mHandler; - - Runnable mDrawCallback = null; - protected boolean mRedrewFlag = true; - - protected void onCreateCommon(final Runnable postDrawCallback) { - mDrawCallback = new Runnable() { - @Override - public void run() { - mRedrewFlag = true; - mHandler.post(postDrawCallback); - }; - }; - getWindow().setBackgroundDrawable(new ColorDrawable(0xffefefef)); - ResourceModifiers.init(getResources()); - - mHardwareView = findViewById(R.id.hardware_view); - mHardwareView.setLayerType(View.LAYER_TYPE_HARDWARE, null); - mHardwareView.setBackgroundColor(Color.WHITE); - mHardwareView.addDrawCallback(mDrawCallback); - - int width = getResources().getDimensionPixelSize(R.dimen.layer_width); - int height = getResources().getDimensionPixelSize(R.dimen.layer_height); - mSoftwareBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - mHardwareBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - - mErrorCalculator = new ErrorCalculator(getApplicationContext(), getResources()); - - mHandler = new Handler(); - } - - protected abstract boolean forceRecreateBitmaps(); - - protected void loadBitmaps() { - Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, "loadBitmaps"); - if (forceRecreateBitmaps()) { - int width = mSoftwareBitmap.getWidth(); - int height = mSoftwareBitmap.getHeight(); - - mSoftwareBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - mHardwareBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - } - - Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, "softwareDraw"); - mHardwareView.draw(new Canvas(mSoftwareBitmap)); - Trace.traceEnd(Trace.TRACE_TAG_ALWAYS); - - try { - Method getHardwareLayer = View.class.getDeclaredMethod("getHardwareLayer"); - if (!getHardwareLayer.isAccessible()) - getHardwareLayer.setAccessible(true); - Object hardwareLayer = getHardwareLayer.invoke(mHardwareView); - if (hardwareLayer == null) { - Log.d(LOG_TAG, "failure to access hardware layer"); - return; - } - Method copyInto = hardwareLayer.getClass() - .getDeclaredMethod("copyInto", Bitmap.class); - if (!copyInto.isAccessible()) - copyInto.setAccessible(true); - - Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, "copyInto"); - boolean success = (Boolean) copyInto.invoke(hardwareLayer, mHardwareBitmap); - Trace.traceEnd(Trace.TRACE_TAG_ALWAYS); - if (!success) { - Log.d(LOG_TAG, "failure to copy hardware layer into bitmap"); - } - } catch (NoSuchMethodException e) { - e.printStackTrace(); - } catch (IllegalArgumentException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } - Trace.traceEnd(Trace.TRACE_TAG_ALWAYS); - } -} diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/DisplayModifier.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/DisplayModifier.java deleted file mode 100644 index 0f4e122d147a..000000000000 --- a/tests/CanvasCompare/src/com/android/test/hwuicompare/DisplayModifier.java +++ /dev/null @@ -1,532 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.test.hwuicompare; - -import java.util.LinkedHashMap; -import java.util.Map.Entry; - -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.RectF; -import android.util.Log; - -public abstract class DisplayModifier { - - // automated tests ignore any combination of operations that don't together return TOTAL_MASK - protected final static int TOTAL_MASK = 0x1F; - - // if we're filling, ensure we're not also sweeping over stroke parameters - protected final static int SWEEP_STROKE_WIDTH_BIT = 0x1 << 0; - protected final static int SWEEP_STROKE_CAP_BIT = 0x1 << 1; - protected final static int SWEEP_STROKE_JOIN_BIT = 0x1 << 2; - - protected final static int SWEEP_SHADER_BIT = 0x1 << 3; // only allow non-simple shaders to use rectangle drawing - protected final static int SWEEP_TRANSFORM_BIT = 0x1 << 4; // only sweep over specified transforms - - abstract public void modifyDrawing(Paint paint, Canvas canvas); - protected int mask() { return 0x0; }; - - private static final RectF gRect = new RectF(0, 0, 200, 175); - private static final float[] gPts = new float[] { - 0, 100, 100, 0, 100, 200, 200, 100 - }; - - private static final int NUM_PARALLEL_LINES = 24; - private static final float[] gTriPts = new float[] { - 75, 0, 130, 130, 130, 130, 0, 130, 0, 130, 75, 0 - }; - private static final float[] gLinePts = new float[NUM_PARALLEL_LINES * 8 + gTriPts.length]; - static { - int index; - for (index = 0; index < gTriPts.length; index++) { - gLinePts[index] = gTriPts[index]; - } - float val = 0; - for (int i = 0; i < NUM_PARALLEL_LINES; i++) { - gLinePts[index + 0] = 150; - gLinePts[index + 1] = val; - gLinePts[index + 2] = 300; - gLinePts[index + 3] = val; - index += 4; - val += 8 + (2.0f/NUM_PARALLEL_LINES); - } - val = 0; - for (int i = 0; i < NUM_PARALLEL_LINES; i++) { - gLinePts[index + 0] = val; - gLinePts[index + 1] = 150; - gLinePts[index + 2] = val; - gLinePts[index + 3] = 300; - index += 4; - val += 8 + (2.0f/NUM_PARALLEL_LINES); - } - }; - - @SuppressWarnings("serial") - private static final LinkedHashMap<String, LinkedHashMap<String, DisplayModifier>> gMaps = new LinkedHashMap<String, LinkedHashMap<String, DisplayModifier>>() { - { - put("aa", new LinkedHashMap<String, DisplayModifier>() { - { - put("true", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - paint.setAntiAlias(true); - } - }); - put("false", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - paint.setAntiAlias(false); - } - }); - } - }); - put("style", new LinkedHashMap<String, DisplayModifier>() { - { - put("fill", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - paint.setStyle(Paint.Style.FILL); - } - }); - put("stroke", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - paint.setStyle(Paint.Style.STROKE); - } - @Override - protected int mask() { return SWEEP_STROKE_WIDTH_BIT; } - }); - put("fillAndStroke", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - paint.setStyle(Paint.Style.FILL_AND_STROKE); - } - - @Override - protected int mask() { return SWEEP_STROKE_WIDTH_BIT; } - }); - } - }); - put("strokeWidth", new LinkedHashMap<String, DisplayModifier>() { - { - put("hair", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - paint.setStrokeWidth(0); - } - @Override - protected int mask() { return SWEEP_STROKE_WIDTH_BIT; } - }); - put("0.3", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - paint.setStrokeWidth(0.3f); - } - }); - put("1", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - paint.setStrokeWidth(1); - } - }); - put("5", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - paint.setStrokeWidth(5); - } - }); - put("30", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - paint.setStrokeWidth(30); - } - }); - } - }); - put("strokeCap", new LinkedHashMap<String, DisplayModifier>() { - { - put("butt", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - paint.setStrokeCap(Paint.Cap.BUTT); - } - @Override - protected int mask() { return SWEEP_STROKE_CAP_BIT; } - }); - put("round", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - paint.setStrokeCap(Paint.Cap.ROUND); - } - }); - put("square", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - paint.setStrokeCap(Paint.Cap.SQUARE); - } - }); - } - }); - put("strokeJoin", new LinkedHashMap<String, DisplayModifier>() { - { - put("bevel", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - paint.setStrokeJoin(Paint.Join.BEVEL); - } - @Override - protected int mask() { return SWEEP_STROKE_JOIN_BIT; } - }); - put("round", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - paint.setStrokeJoin(Paint.Join.ROUND); - } - }); - put("miter", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - paint.setStrokeJoin(Paint.Join.MITER); - } - }); - // TODO: add miter0, miter1 etc to test miter distances - } - }); - - put("transform", new LinkedHashMap<String, DisplayModifier>() { - { - put("noTransform", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) {} - @Override - protected int mask() { return SWEEP_TRANSFORM_BIT; }; - }); - put("rotate5", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - canvas.rotate(5); - } - }); - put("rotate45", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - canvas.rotate(45); - } - }); - put("rotate90", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - canvas.rotate(90); - canvas.translate(0, -200); - } - }); - put("scale2x2", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - canvas.scale(2, 2); - } - @Override - protected int mask() { return SWEEP_TRANSFORM_BIT; }; - }); - put("rot20scl1x4", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - canvas.rotate(20); - canvas.scale(1, 4); - } - @Override - protected int mask() { return SWEEP_TRANSFORM_BIT; }; - }); - } - }); - - put("shader", new LinkedHashMap<String, DisplayModifier>() { - { - put("noShader", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) {} - @Override - protected int mask() { return SWEEP_SHADER_BIT; }; - }); - put("repeatShader", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - paint.setShader(ResourceModifiers.instance().mRepeatShader); - } - @Override - protected int mask() { return SWEEP_SHADER_BIT; }; - }); - put("translatedShader", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - paint.setShader(ResourceModifiers.instance().mTranslatedShader); - } - }); - put("scaledShader", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - paint.setShader(ResourceModifiers.instance().mScaledShader); - } - }); - put("horGradient", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - paint.setShader(ResourceModifiers.instance().mHorGradient); - } - }); - put("diagGradient", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - paint.setShader(ResourceModifiers.instance().mDiagGradient); - } - @Override - protected int mask() { return SWEEP_SHADER_BIT; }; - }); - put("vertGradient", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - paint.setShader(ResourceModifiers.instance().mVertGradient); - } - }); - put("radGradient", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - paint.setShader(ResourceModifiers.instance().mRadGradient); - } - }); - put("sweepGradient", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - paint.setShader(ResourceModifiers.instance().mSweepGradient); - } - }); - put("composeShader", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - paint.setShader(ResourceModifiers.instance().mComposeShader); - } - }); - put("bad composeShader", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - paint.setShader(ResourceModifiers.instance().mBadComposeShader); - } - }); - put("bad composeShader 2", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - paint.setShader(ResourceModifiers.instance().mAnotherBadComposeShader); - } - }); - } - }); - - // FINAL MAP: DOES ACTUAL DRAWING - put("drawing", new LinkedHashMap<String, DisplayModifier>() { - { - put("roundRect", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - canvas.drawRoundRect(gRect, 20, 20, paint); - } - }); - put("rect", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - canvas.drawRect(gRect, paint); - } - @Override - protected int mask() { return SWEEP_SHADER_BIT | SWEEP_STROKE_CAP_BIT; }; - }); - put("circle", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - canvas.drawCircle(100, 100, 75, paint); - } - }); - put("oval", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - canvas.drawOval(gRect, paint); - } - }); - put("lines", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - canvas.drawLines(gLinePts, paint); - } - @Override - protected int mask() { return SWEEP_STROKE_CAP_BIT; }; - }); - put("plusPoints", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - canvas.drawPoints(gPts, paint); - } - }); - put("text", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - paint.setTextSize(36); - canvas.drawText("TEXTTEST", 0, 50, paint); - } - }); - put("shadowtext", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - paint.setTextSize(36); - paint.setShadowLayer(3.0f, 0.0f, 3.0f, 0xffff00ff); - canvas.drawText("TEXTTEST", 0, 50, paint); - } - }); - put("bitmapMesh", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - canvas.drawBitmapMesh(ResourceModifiers.instance().mBitmap, 3, 3, - ResourceModifiers.instance().mBitmapVertices, 0, null, 0, null); - } - }); - put("arc", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - canvas.drawArc(gRect, 260, 285, false, paint); - } - @Override - protected int mask() { return SWEEP_STROKE_CAP_BIT; }; - }); - put("arcFromCenter", new DisplayModifier() { - @Override - public void modifyDrawing(Paint paint, Canvas canvas) { - canvas.drawArc(gRect, 260, 285, true, paint); - } - @Override - protected int mask() { return SWEEP_STROKE_JOIN_BIT; }; - }); - } - }); - // WARNING: DON'T PUT MORE MAPS BELOW THIS - } - }; - - private static LinkedHashMap<String, DisplayModifier> getMapAtIndex(int index) { - for (LinkedHashMap<String, DisplayModifier> map : gMaps.values()) { - if (index == 0) { - return map; - } - index--; - } - return null; - } - - // indices instead of iterators for easier bidirectional traversal - private static final int mIndices[] = new int[gMaps.size()]; - private static final String[] mLastAppliedModifications = new String[gMaps.size()]; - - private static boolean stepInternal(boolean forward) { - int modifierMapIndex = gMaps.size() - 1; - while (modifierMapIndex >= 0) { - LinkedHashMap<String, DisplayModifier> map = getMapAtIndex(modifierMapIndex); - mIndices[modifierMapIndex] += (forward ? 1 : -1); - - if (mIndices[modifierMapIndex] >= 0 && mIndices[modifierMapIndex] < map.size()) { - break; - } - - mIndices[modifierMapIndex] = (forward ? 0 : map.size() - 1); - modifierMapIndex--; - } - return modifierMapIndex < 0; // true if resetting - } - - public static boolean step() { - boolean ret = false; - do { - ret |= stepInternal(true); - } while (!checkModificationStateMask()); - return ret; - } - - public static boolean stepBack() { - boolean ret = false; - do { - ret |= stepInternal(false); - } while (!checkModificationStateMask()); - return ret; - } - - private static boolean checkModificationStateMask() { - int operatorMask = 0x0; - int mapIndex = 0; - for (LinkedHashMap<String, DisplayModifier> map : gMaps.values()) { - int displayModifierIndex = mIndices[mapIndex]; - for (Entry<String, DisplayModifier> modifierEntry : map.entrySet()) { - if (displayModifierIndex == 0) { - mLastAppliedModifications[mapIndex] = modifierEntry.getKey(); - operatorMask |= modifierEntry.getValue().mask(); - break; - } - displayModifierIndex--; - } - mapIndex++; - } - return operatorMask == TOTAL_MASK; - } - - public static void apply(Paint paint, Canvas canvas) { - int mapIndex = 0; - for (LinkedHashMap<String, DisplayModifier> map : gMaps.values()) { - int displayModifierIndex = mIndices[mapIndex]; - for (Entry<String, DisplayModifier> modifierEntry : map.entrySet()) { - if (displayModifierIndex == 0) { - mLastAppliedModifications[mapIndex] = modifierEntry.getKey(); - modifierEntry.getValue().modifyDrawing(paint, canvas); - break; - } - displayModifierIndex--; - } - mapIndex++; - } - } - - public static String[] getLastAppliedModifications() { - return mLastAppliedModifications.clone(); - } - - public static String[][] getStrings() { - String[][] keys = new String[gMaps.size()][]; - - int i = 0; - for (LinkedHashMap<String, DisplayModifier> map : gMaps.values()) { - keys[i] = new String[map.size()]; - int j = 0; - for (String key : map.keySet()) { - keys[i][j++] = key; - } - i++; - } - - return keys; - } - - public static void setIndex(int mapIndex, int newIndexValue) { - mIndices[mapIndex] = newIndexValue; - } - - public static int[] getIndices() { - return mIndices; - } -} diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/ErrorCalculator.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/ErrorCalculator.java deleted file mode 100644 index d402699b0979..000000000000 --- a/tests/CanvasCompare/src/com/android/test/hwuicompare/ErrorCalculator.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.test.hwuicompare; - -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.Color; -import android.renderscript.Allocation; -import android.renderscript.Element; -import android.renderscript.RenderScript; -import android.util.Log; - -public class ErrorCalculator { - private static final String LOG_TAG = "ErrorCalculator"; - private static final int REGION_SIZE = 8; - - private static final boolean LOG_TIMING = false; - private static final boolean LOG_CALC = false; - - private RenderScript mRS; - private Allocation mIdealPixelsAllocation; - private Allocation mGivenPixelsAllocation; - private Allocation mOutputPixelsAllocation; - - private Allocation mInputRowsAllocation; - private Allocation mOutputRegionsAllocation; - - private ScriptC_errorCalculator mScript; - - private int[] mOutputRowRegions; - - public ErrorCalculator(Context c, Resources resources) { - int width = resources.getDimensionPixelSize(R.dimen.layer_width); - int height = resources.getDimensionPixelSize(R.dimen.layer_height); - mOutputRowRegions = new int[height / REGION_SIZE]; - - mRS = RenderScript.create(c); - int[] rowIndices = new int[height / REGION_SIZE]; - for (int i = 0; i < rowIndices.length; i++) - rowIndices[i] = i * REGION_SIZE; - - mScript = new ScriptC_errorCalculator(mRS); - mScript.set_HEIGHT(height); - mScript.set_WIDTH(width); - mScript.set_REGION_SIZE(REGION_SIZE); - - mInputRowsAllocation = Allocation.createSized(mRS, Element.I32(mRS), rowIndices.length, - Allocation.USAGE_SCRIPT); - mInputRowsAllocation.copyFrom(rowIndices); - mOutputRegionsAllocation = Allocation.createSized(mRS, Element.I32(mRS), - mOutputRowRegions.length, Allocation.USAGE_SCRIPT); - } - - - private static long startMillis, middleMillis; - - public float calcErrorRS(Bitmap ideal, Bitmap given) { - if (LOG_TIMING) { - startMillis = System.currentTimeMillis(); - } - - mIdealPixelsAllocation = Allocation.createFromBitmap(mRS, ideal, - Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT); - mGivenPixelsAllocation = Allocation.createFromBitmap(mRS, given, - Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT); - - mScript.set_ideal(mIdealPixelsAllocation); - mScript.set_given(mGivenPixelsAllocation); - - mScript.forEach_countInterestingRegions(mInputRowsAllocation, mOutputRegionsAllocation); - mOutputRegionsAllocation.copyTo(mOutputRowRegions); - - int regionCount = 0; - for (int region : mOutputRowRegions) { - regionCount += region; - } - int interestingPixels = Math.max(1, regionCount) * REGION_SIZE * REGION_SIZE; - - if (LOG_TIMING) { - long startMillis2 = System.currentTimeMillis(); - } - - mScript.forEach_accumulateError(mInputRowsAllocation, mOutputRegionsAllocation); - mOutputRegionsAllocation.copyTo(mOutputRowRegions); - float totalError = 0; - for (int row : mOutputRowRegions) { - totalError += row; - } - totalError /= 1024.0f; - - if (LOG_TIMING) { - long finalMillis = System.currentTimeMillis(); - Log.d(LOG_TAG, "rs: first part took " + (middleMillis - startMillis) + "ms"); - Log.d(LOG_TAG, "rs: last part took " + (finalMillis - middleMillis) + "ms"); - } - if (LOG_CALC) { - Log.d(LOG_TAG, "rs: error " + totalError + ", pixels " + interestingPixels); - } - return totalError / interestingPixels; - } - - public void calcErrorHeatmapRS(Bitmap ideal, Bitmap given, Bitmap output) { - mIdealPixelsAllocation = Allocation.createFromBitmap(mRS, ideal, - Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT); - mGivenPixelsAllocation = Allocation.createFromBitmap(mRS, given, - Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT); - - mScript.set_ideal(mIdealPixelsAllocation); - mScript.set_given(mGivenPixelsAllocation); - - mOutputPixelsAllocation = Allocation.createFromBitmap(mRS, output, - Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT); - mScript.forEach_displayDifference(mOutputPixelsAllocation, mOutputPixelsAllocation); - mOutputPixelsAllocation.copyTo(output); - } - - public static float calcError(Bitmap ideal, Bitmap given) { - if (LOG_TIMING) { - startMillis = System.currentTimeMillis(); - } - - int interestingRegions = 0; - for (int x = 0; x < ideal.getWidth(); x += REGION_SIZE) { - for (int y = 0; y < ideal.getWidth(); y += REGION_SIZE) { - if (inspectRegion(ideal, x, y)) { - interestingRegions++; - } - } - } - - int interestingPixels = Math.max(1, interestingRegions) * REGION_SIZE * REGION_SIZE; - - if (LOG_TIMING) { - long startMillis2 = System.currentTimeMillis(); - } - - float totalError = 0; - for (int x = 0; x < ideal.getWidth(); x++) { - for (int y = 0; y < ideal.getHeight(); y++) { - int idealColor = ideal.getPixel(x, y); - int givenColor = given.getPixel(x, y); - if (idealColor == givenColor) - continue; - totalError += Math.abs(Color.red(idealColor) - Color.red(givenColor)); - totalError += Math.abs(Color.green(idealColor) - Color.green(givenColor)); - totalError += Math.abs(Color.blue(idealColor) - Color.blue(givenColor)); - totalError += Math.abs(Color.alpha(idealColor) - Color.alpha(givenColor)); - } - } - totalError /= 1024.0f; - if (LOG_TIMING) { - long finalMillis = System.currentTimeMillis(); - Log.d(LOG_TAG, "dvk: first part took " + (middleMillis - startMillis) + "ms"); - Log.d(LOG_TAG, "dvk: last part took " + (finalMillis - middleMillis) + "ms"); - } - if (LOG_CALC) { - Log.d(LOG_TAG, "dvk: error " + totalError + ", pixels " + interestingPixels); - } - return totalError / interestingPixels; - } - - private static boolean inspectRegion(Bitmap ideal, int x, int y) { - int regionColor = ideal.getPixel(x, y); - for (int i = 0; i < REGION_SIZE; i++) { - for (int j = 0; j < REGION_SIZE; j++) { - if (ideal.getPixel(x + i, y + j) != regionColor) - return true; - } - } - return false; - } -} diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/MainView.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/MainView.java deleted file mode 100644 index 454fe7b50ad7..000000000000 --- a/tests/CanvasCompare/src/com/android/test/hwuicompare/MainView.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.test.hwuicompare; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.util.AttributeSet; -import android.view.View; - -public class MainView extends View { - Paint mPaint = new Paint(); - - public MainView(Context context) { - super(context); - } - - public MainView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public MainView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - - mPaint.reset(); - DisplayModifier.apply(mPaint, canvas); - - if (mDrawCallback != null) { - mDrawCallback.run(); - } - } - - private Runnable mDrawCallback; - public void addDrawCallback(Runnable drawCallback) { - mDrawCallback = drawCallback; - } -} diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/ManualActivity.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/ManualActivity.java deleted file mode 100644 index 405ff65a34fd..000000000000 --- a/tests/CanvasCompare/src/com/android/test/hwuicompare/ManualActivity.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.test.hwuicompare; - -import com.android.test.hwuicompare.R; - -import android.graphics.Bitmap; -import android.graphics.Color; -import android.os.Bundle; -import android.util.Log; -import android.view.View; -import android.view.View.OnClickListener; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.ImageButton; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.Spinner; -import android.widget.TextView; - -public class ManualActivity extends CompareActivity { - private static final String LOG_TAG = "ManualActivity"; - private ImageView mCompareImageView; - private Bitmap mCompareBitmap; - private TextView mErrorTextView; - private MainView mSoftwareView; - - private static final int COMPARE_VIEW_UNINITIALIZED = -1; - private static final int COMPARE_VIEW_HARDWARE = 0; - private static final int COMPARE_VIEW_SOFTWARE = 1; - private static final int COMPARE_VIEW_HEATMAP = 2; // TODO: add more like this? any ideas? - - private int mCompareImageViewState = COMPARE_VIEW_UNINITIALIZED; - private int mLastCompareImageViewState = COMPARE_VIEW_UNINITIALIZED; - - Runnable mRunnable = new Runnable() { - @Override - public void run() { - Log.d(LOG_TAG, "mRunnable running, mRedrewFlag = " + mRedrewFlag); - - if (mRedrewFlag) { - loadBitmaps(); - // recalculate error - float error = mErrorCalculator.calcErrorRS(mSoftwareBitmap, mHardwareBitmap); - String modname = ""; - for (String s : DisplayModifier.getLastAppliedModifications()) { - modname = modname.concat(s + "."); - } - - Log.d(LOG_TAG, "error for " + modname + " is " + error); - mErrorTextView.setText(String.format("%.4f", error)); - } - - if (mCompareImageViewState != mLastCompareImageViewState || mRedrewFlag) { - switch (mCompareImageViewState) { - case COMPARE_VIEW_UNINITIALIZED: - // set to hardware - case COMPARE_VIEW_HARDWARE: - mCompareImageView.setImageBitmap(mHardwareBitmap); - break; - case COMPARE_VIEW_SOFTWARE: - mCompareImageView.setImageBitmap(mSoftwareBitmap); - break; - case COMPARE_VIEW_HEATMAP: - mErrorCalculator.calcErrorHeatmapRS(mSoftwareBitmap, mHardwareBitmap, - mCompareBitmap); - mCompareImageView.setImageBitmap(mCompareBitmap); - break; - } - mCompareImageView.getDrawable().setFilterBitmap(false); - mCompareImageView.invalidate(); - } - - mLastCompareImageViewState = mCompareImageViewState; - mRedrewFlag = false; - mHandler.removeCallbacks(mRunnable); - } - }; - - private void redrawViews() { - mHardwareView.invalidate(); - mSoftwareView.invalidate(); - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.manual_layout); - onCreateCommon(mRunnable); - - mSoftwareView = (MainView) findViewById(R.id.software_view); - mSoftwareView.setLayerType(View.LAYER_TYPE_SOFTWARE, null); - mSoftwareView.setBackgroundColor(Color.WHITE); - mSoftwareView.addDrawCallback(mDrawCallback); - - mCompareImageView = (ImageView) findViewById(R.id.compare_image_view); - - int width = getResources().getDimensionPixelSize(R.dimen.layer_width); - int height = getResources().getDimensionPixelSize(R.dimen.layer_height); - mCompareBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - - mErrorTextView = (TextView) findViewById(R.id.current_error); - ((ImageButton) findViewById(R.id.next)).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - DisplayModifier.step(); - updateSpinners(); - redrawViews(); - } - }); - ((ImageButton) findViewById(R.id.previous)).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - DisplayModifier.stepBack(); - updateSpinners(); - redrawViews(); - } - }); - ((Button) findViewById(R.id.show_hardware_version)) - .setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - mCompareImageViewState = COMPARE_VIEW_HARDWARE; - mHandler.post(mRunnable); - } - }); - ((Button) findViewById(R.id.show_software_version)) - .setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - mCompareImageViewState = COMPARE_VIEW_SOFTWARE; - mHandler.post(mRunnable); - } - }); - ((Button) findViewById(R.id.show_error_heatmap)).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - mCompareImageViewState = COMPARE_VIEW_HEATMAP; - mHandler.post(mRunnable); - } - }); - - buildSpinnerLayout(); - } - - private class DisplayModifierSpinner extends Spinner { - private final int mIndex; - - public DisplayModifierSpinner(int index) { - super(ManualActivity.this); - mIndex = index; - setOnItemSelectedListener(new OnItemSelectedListener() { - - @Override - public void onItemSelected(AdapterView<?> parentView, View selectedItem, - int position, long id) { - DisplayModifier.setIndex(mIndex, position); - redrawViews(); - } - - @Override - public void onNothingSelected(AdapterView<?> parentView) { - } - }); - } - } - - private Spinner[] mSpinners; - - private void buildSpinnerLayout() { - LinearLayout layout = (LinearLayout) findViewById(R.id.spinner_layout); - String[][] mapsStrings = DisplayModifier.getStrings(); - mSpinners = new Spinner[mapsStrings.length]; - int index = 0; - for (String[] spinnerValues : mapsStrings) { - mSpinners[index] = new DisplayModifierSpinner(index); - mSpinners[index].setAdapter(new ArrayAdapter<String>(this, - android.R.layout.simple_spinner_dropdown_item, spinnerValues)); - layout.addView(mSpinners[index]); - index++; - } - Log.d(LOG_TAG, "created " + index + " spinners"); - } - - private void updateSpinners() { - int[] indices = DisplayModifier.getIndices(); - for (int i = 0; i < mSpinners.length; i++) { - mSpinners[i].setSelection(indices[i]); - } - } - - @Override - protected boolean forceRecreateBitmaps() { - // continually recreate bitmaps to avoid modifying bitmaps currently being drawn - return true; - } -} diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/ResourceModifiers.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/ResourceModifiers.java deleted file mode 100644 index d5224813c0bc..000000000000 --- a/tests/CanvasCompare/src/com/android/test/hwuicompare/ResourceModifiers.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.test.hwuicompare; - -import com.android.test.hwuicompare.R; - -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.BitmapShader; -import android.graphics.Color; -import android.graphics.ComposeShader; -import android.graphics.LinearGradient; -import android.graphics.PorterDuff; -import android.graphics.RadialGradient; -import android.graphics.SweepGradient; -import android.graphics.Matrix; -import android.graphics.Shader; - -public class ResourceModifiers { - public final BitmapShader mRepeatShader; - public final BitmapShader mTranslatedShader; - public final BitmapShader mScaledShader; - private final int mTexWidth; - private final int mTexHeight; - private final float mDrawWidth; - private final float mDrawHeight; - public final LinearGradient mHorGradient; - public final LinearGradient mDiagGradient; - public final LinearGradient mVertGradient; - public final RadialGradient mRadGradient; - public final SweepGradient mSweepGradient; - public final ComposeShader mComposeShader; - public final ComposeShader mBadComposeShader; - public final ComposeShader mAnotherBadComposeShader; - public final Bitmap mBitmap; - private final Matrix mMtx1; - private final Matrix mMtx2; - private final Matrix mMtx3; - - public final float[] mBitmapVertices; - public final int[] mBitmapColors; - - private static ResourceModifiers sInstance = null; - public static ResourceModifiers instance() { return sInstance; } - public static void init(Resources resources) { - sInstance = new ResourceModifiers(resources); - } - - public ResourceModifiers(Resources resources) { - mBitmap = BitmapFactory.decodeResource(resources, R.drawable.sunset1); - mTexWidth = mBitmap.getWidth(); - mTexHeight = mBitmap.getHeight(); - - mDrawWidth = resources.getDimensionPixelSize(R.dimen.layer_width); - mDrawHeight = resources.getDimensionPixelSize(R.dimen.layer_height); - - mRepeatShader = new BitmapShader(mBitmap, Shader.TileMode.REPEAT, - Shader.TileMode.REPEAT); - - mTranslatedShader = new BitmapShader(mBitmap, Shader.TileMode.REPEAT, - Shader.TileMode.REPEAT); - mMtx1 = new Matrix(); - mMtx1.setTranslate(mTexWidth / 2.0f, mTexHeight / 2.0f); - mMtx1.postRotate(45, 0, 0); - mTranslatedShader.setLocalMatrix(mMtx1); - - mScaledShader = new BitmapShader(mBitmap, Shader.TileMode.MIRROR, - Shader.TileMode.MIRROR); - mMtx2 = new Matrix(); - mMtx2.setScale(0.5f, 0.5f); - mScaledShader.setLocalMatrix(mMtx2); - - mHorGradient = new LinearGradient(0.0f, 0.0f, 1.0f, 0.0f, - Color.RED, Color.GREEN, Shader.TileMode.CLAMP); - mMtx3 = new Matrix(); - mMtx3.setScale(mDrawHeight, 1.0f); - mMtx3.postRotate(-90.0f); - mMtx3.postTranslate(0.0f, mDrawHeight); - mHorGradient.setLocalMatrix(mMtx3); - - mDiagGradient = new LinearGradient(0.0f, 0.0f, mDrawWidth / 2.0f, mDrawHeight / 2.0f, - Color.BLUE, Color.RED, Shader.TileMode.CLAMP); - - mVertGradient = new LinearGradient(0.0f, 0.0f, 0.0f, mDrawHeight / 2.0f, - Color.YELLOW, Color.MAGENTA, Shader.TileMode.MIRROR); - - mSweepGradient = new SweepGradient(mDrawWidth / 2.0f, mDrawHeight / 2.0f, - Color.YELLOW, Color.MAGENTA); - - mComposeShader = new ComposeShader(mRepeatShader, mHorGradient, - PorterDuff.Mode.MULTIPLY); - - final float width = mBitmap.getWidth() / 8.0f; - final float height = mBitmap.getHeight() / 8.0f; - - mBitmapVertices = new float[] { - 0.0f, 0.0f, width, 0.0f, width * 2, 0.0f, width * 3, 0.0f, - 0.0f, height, width, height, width * 2, height, width * 4, height, - 0.0f, height * 2, width, height * 2, width * 2, height * 2, width * 3, height * 2, - 0.0f, height * 4, width, height * 4, width * 2, height * 4, width * 4, height * 4, - }; - - mBitmapColors = new int[] { - 0xffff0000, 0xff00ff00, 0xff0000ff, 0xffff0000, - 0xff0000ff, 0xffff0000, 0xff00ff00, 0xff00ff00, - 0xff00ff00, 0xff0000ff, 0xffff0000, 0xff00ff00, - 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00ff0000, - }; - - // Use a repeating gradient with many colors to test the non simple case. - mRadGradient = new RadialGradient(mDrawWidth / 4.0f, mDrawHeight / 4.0f, 4.0f, - mBitmapColors, null, Shader.TileMode.REPEAT); - - mBadComposeShader = new ComposeShader(mRadGradient, mComposeShader, - PorterDuff.Mode.MULTIPLY); - - mAnotherBadComposeShader = new ComposeShader(mRadGradient, mVertGradient, - PorterDuff.Mode.MULTIPLY); - } - -} diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/Test.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/Test.java deleted file mode 100644 index 1ff153c003c1..000000000000 --- a/tests/CanvasCompare/src/com/android/test/hwuicompare/Test.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.android.test.hwuicompare; - -import com.android.test.hwuicompare.AutomaticActivity.FinalCallback; - -import android.os.Bundle; -import android.test.ActivityInstrumentationTestCase2; - -public class Test extends ActivityInstrumentationTestCase2<AutomaticActivity> { - AutomaticActivity mActivity; - private Bundle mBundle; - - public Test() { - super(AutomaticActivity.class); - } - - @Override - protected void setUp() throws Exception { - super.setUp(); - mBundle = new Bundle(); - mActivity = getActivity(); - mActivity.setFinalCallback(new FinalCallback() { - - @Override - void report(String key, float value) { - mBundle.putFloat(key, value); - } - @Override - void complete() { - synchronized(mBundle) { - mBundle.notify(); - } - } - }); - } - - public void testCanvas() { - synchronized(mBundle) { - try { - mBundle.wait(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - getInstrumentation().sendStatus(0, mBundle); - } -} diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/errorCalculator.rscript b/tests/CanvasCompare/src/com/android/test/hwuicompare/errorCalculator.rscript deleted file mode 100644 index 0a1742ef3867..000000000000 --- a/tests/CanvasCompare/src/com/android/test/hwuicompare/errorCalculator.rscript +++ /dev/null @@ -1,61 +0,0 @@ -#pragma version(1) -#pragma rs java_package_name(com.android.test.hwuicompare) - -int REGION_SIZE; -int WIDTH; -int HEIGHT; - -rs_allocation ideal; -rs_allocation given; - -void countInterestingRegions(const int32_t *v_in, int32_t *v_out) { - int y = v_in[0]; - v_out[0] = 0; - - for (int x = 0; x < HEIGHT; x += REGION_SIZE) { - bool interestingRegion = false; - uchar4 regionColor = rsGetElementAt_uchar4(ideal, x, y); - for (int i = 0; i < REGION_SIZE && !interestingRegion; i++) { - for (int j = 0; j < REGION_SIZE && !interestingRegion; j++) { - uchar4 testVal = rsGetElementAt_uchar4(ideal, x + j, y + i); - interestingRegion |= (testVal.r != regionColor.r); - interestingRegion |= (testVal.g != regionColor.g); - interestingRegion |= (testVal.b != regionColor.b); - interestingRegion |= (testVal.a != regionColor.a); - } - } - if (interestingRegion) { - v_out[0]++; - } - } -} - -void accumulateError(const int32_t *v_in, int32_t *v_out) { - int startY = v_in[0]; - int error = 0; - for (int y = startY; y < startY + REGION_SIZE; y++) { - for (int x = 0; x < HEIGHT; x++) { - uchar4 idealPixel = rsGetElementAt_uchar4(ideal, x, y); - uchar4 givenPixel = rsGetElementAt_uchar4(given, x, y); - - error += abs(idealPixel.x - givenPixel.x); - error += abs(idealPixel.y - givenPixel.y); - error += abs(idealPixel.z - givenPixel.z); - error += abs(idealPixel.w - givenPixel.w); - } - } - v_out[0] = error; -} - -void displayDifference(const uchar4 *v_in, uchar4 *v_out, uint32_t x, uint32_t y) { - float4 idealPixel = rsGetElementAt_float4(ideal, x, y); - float4 givenPixel = rsGetElementAt_float4(given, x, y); - - float4 diff = idealPixel - givenPixel; - float totalDiff = diff.x + diff.y + diff.z + diff.w; - if (totalDiff < 0) { - v_out[0] = rsPackColorTo8888(0, 0, clamp(-totalDiff/2.f, 0.f, 1.f)); - } else { - v_out[0] = rsPackColorTo8888(clamp(totalDiff/2.f, 0.f, 1.f), 0, 0); - } -} diff --git a/tests/Codegen/Android.bp b/tests/Codegen/Android.bp index ddbf16817b94..7fbe3b37f99e 100644 --- a/tests/Codegen/Android.bp +++ b/tests/Codegen/Android.bp @@ -24,6 +24,14 @@ android_test { plugins: [ "staledataclass-annotation-processor", ], + // Exports needed for staledataclass-annotation-processor, see b/139342589. + javacflags: [ + "-J--add-modules=jdk.compiler", + "-J--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED", + "-J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED", + "-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", + "-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", + ], static_libs: [ "junit", "hamcrest", diff --git a/tests/DynamicCodeLoggerIntegrationTests/src/com/android/server/pm/dex/DynamicCodeLoggerIntegrationTests.java b/tests/DynamicCodeLoggerIntegrationTests/src/com/android/server/pm/dex/DynamicCodeLoggerIntegrationTests.java index 5430dee5ca31..afa4ff80b6e3 100644 --- a/tests/DynamicCodeLoggerIntegrationTests/src/com/android/server/pm/dex/DynamicCodeLoggerIntegrationTests.java +++ b/tests/DynamicCodeLoggerIntegrationTests/src/com/android/server/pm/dex/DynamicCodeLoggerIntegrationTests.java @@ -84,7 +84,7 @@ public final class DynamicCodeLoggerIntegrationTests { // avoid flakiness we run these tests multiple times, allowing progressively longer between // code loading and checking the logs on each try.) private static final int AUDIT_LOG_RETRIES = 10; - private static final int RETRY_DELAY_MS = 2_000; + private static final int RETRY_DELAY_MS = 500; private static Context sContext; private static int sMyUid; @@ -245,7 +245,7 @@ public final class DynamicCodeLoggerIntegrationTests { "/DynamicCodeLoggerNativeExecutable", privateCopyFile); EventLog.writeEvent(EventLog.getTagCode("auditd"), - "type=1400 avc: granted { execute_no_trans } " + "type=1400 avc: granted { execute_no_trans } " + "path=\"" + privateCopyFile + "\" " + "scontext=u:r:untrusted_app: " + "tcontext=u:object_r:app_data_file: " diff --git a/tests/EnforcePermission/Android.bp b/tests/EnforcePermission/Android.bp new file mode 100644 index 000000000000..719a89817a9d --- /dev/null +++ b/tests/EnforcePermission/Android.bp @@ -0,0 +1,22 @@ +// Copyright (C) 2023 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 { + default_applicable_licenses: ["frameworks_base_license"], +} + +filegroup { + name: "frameworks-enforce-permission-test-aidl", + srcs: ["aidl/**/*.aidl"], +} diff --git a/tests/EnforcePermission/OWNERS b/tests/EnforcePermission/OWNERS new file mode 100644 index 000000000000..39550a394f33 --- /dev/null +++ b/tests/EnforcePermission/OWNERS @@ -0,0 +1,3 @@ +# Bug component: 315013 +tweek@google.com +brufino@google.com diff --git a/tests/EnforcePermission/TEST_MAPPING b/tests/EnforcePermission/TEST_MAPPING new file mode 100644 index 000000000000..a1bf42a44e86 --- /dev/null +++ b/tests/EnforcePermission/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "EnforcePermissionTests" + } + ] +} diff --git a/tests/EnforcePermission/aidl/android/tests/enforcepermission/INested.aidl b/tests/EnforcePermission/aidl/android/tests/enforcepermission/INested.aidl new file mode 100644 index 000000000000..1eb773dc19b8 --- /dev/null +++ b/tests/EnforcePermission/aidl/android/tests/enforcepermission/INested.aidl @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2023 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.tests.enforcepermission; + +interface INested { + @EnforcePermission("ACCESS_NETWORK_STATE") + void ProtectedByAccessNetworkState(); + + @EnforcePermission("READ_SYNC_SETTINGS") + void ProtectedByReadSyncSettings(); +} diff --git a/tests/EnforcePermission/aidl/android/tests/enforcepermission/IProtected.aidl b/tests/EnforcePermission/aidl/android/tests/enforcepermission/IProtected.aidl new file mode 100644 index 000000000000..18e3aecfa832 --- /dev/null +++ b/tests/EnforcePermission/aidl/android/tests/enforcepermission/IProtected.aidl @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023 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.tests.enforcepermission; + +interface IProtected { + @EnforcePermission("INTERNET") + void ProtectedByInternet(); + + @EnforcePermission("VIBRATE") + void ProtectedByVibrate(); + + @EnforcePermission("INTERNET") + void ProtectedByInternetAndVibrateImplicitly(); + + @EnforcePermission("INTERNET") + void ProtectedByInternetAndAccessNetworkStateImplicitly(); + + @EnforcePermission("INTERNET") + void ProtectedByInternetAndReadSyncSettingsImplicitly(); +} diff --git a/tests/EnforcePermission/service-app/Android.bp b/tests/EnforcePermission/service-app/Android.bp new file mode 100644 index 000000000000..a4ac1d7c6134 --- /dev/null +++ b/tests/EnforcePermission/service-app/Android.bp @@ -0,0 +1,32 @@ +// Copyright (C) 2023 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 { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +android_test_helper_app { + name: "EnforcePermissionTestHelper", + srcs: [ + "src/**/*.java", + ":frameworks-enforce-permission-test-aidl", + ], + platform_apis: true, + certificate: "platform", +} diff --git a/tests/CanvasCompare/res/values/values.xml b/tests/EnforcePermission/service-app/AndroidManifest.xml index f69378d34f65..ddafe15ab88f 100644 --- a/tests/CanvasCompare/res/values/values.xml +++ b/tests/EnforcePermission/service-app/AndroidManifest.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2012 The Android Open Source Project +<!-- Copyright (C) 2023 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. @@ -13,13 +13,15 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android.tests.enforcepermission.service"> + <application> + <service + android:name=".TestService" + android:exported="true" /> - <!-- NOTE: the below MUST be multiples of 64 --> - <dimen name="layer_height">320px</dimen> - <dimen name="layer_width">320px</dimen> - - <dimen name="layer_height_double">640px</dimen> - <dimen name="layer_width_double">640px</dimen> - -</resources> + <service + android:name=".NestedTestService" + android:exported="true" /> + </application> +</manifest> diff --git a/tests/EnforcePermission/service-app/src/android/tests/enforcepermission/service/NestedTestService.java b/tests/EnforcePermission/service-app/src/android/tests/enforcepermission/service/NestedTestService.java new file mode 100644 index 000000000000..7879a1214c01 --- /dev/null +++ b/tests/EnforcePermission/service-app/src/android/tests/enforcepermission/service/NestedTestService.java @@ -0,0 +1,48 @@ +/** + * Copyright (C) 2023 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.tests.enforcepermission.service; + +import android.annotation.EnforcePermission; +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; +import android.tests.enforcepermission.INested; +import android.util.Log; + +public class NestedTestService extends Service { + private static final String TAG = "EnforcePermission.NestedTestService"; + + @Override + public IBinder onBind(Intent intent) { + Log.i(TAG, "onBind"); + return mBinder; + } + + private final INested.Stub mBinder = new INested.Stub() { + @Override + @EnforcePermission(android.Manifest.permission.ACCESS_NETWORK_STATE) + public void ProtectedByAccessNetworkState() { + ProtectedByAccessNetworkState_enforcePermission(); + } + + @Override + @EnforcePermission(android.Manifest.permission.READ_SYNC_SETTINGS) + public void ProtectedByReadSyncSettings() { + ProtectedByReadSyncSettings_enforcePermission(); + } + }; +} diff --git a/tests/EnforcePermission/service-app/src/android/tests/enforcepermission/service/TestService.java b/tests/EnforcePermission/service-app/src/android/tests/enforcepermission/service/TestService.java new file mode 100644 index 000000000000..e9b897db1294 --- /dev/null +++ b/tests/EnforcePermission/service-app/src/android/tests/enforcepermission/service/TestService.java @@ -0,0 +1,119 @@ +/** + * Copyright (C) 2023 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.tests.enforcepermission.service; + +import android.annotation.EnforcePermission; +import android.app.Service; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.os.IBinder; +import android.os.RemoteException; +import android.tests.enforcepermission.INested; +import android.tests.enforcepermission.IProtected; +import android.util.Log; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +public class TestService extends Service { + + private static final String TAG = "EnforcePermission.TestService"; + private volatile ServiceConnection mNestedServiceConnection; + + @Override + public void onCreate() { + mNestedServiceConnection = new ServiceConnection(); + Intent intent = new Intent(this, NestedTestService.class); + boolean bound = bindService(intent, mNestedServiceConnection, Context.BIND_AUTO_CREATE); + if (!bound) { + Log.wtf(TAG, "bindService() on NestedTestService failed"); + } + } + + @Override + public void onDestroy() { + unbindService(mNestedServiceConnection); + } + + private static final class ServiceConnection implements android.content.ServiceConnection { + private volatile CompletableFuture<INested> mFuture = new CompletableFuture<>(); + + public INested get() { + try { + return mFuture.get(1, TimeUnit.SECONDS); + } catch (ExecutionException | InterruptedException | TimeoutException e) { + throw new RuntimeException("Unable to reach NestedTestService: " + e.getMessage()); + } + } + + public void onServiceConnected(ComponentName className, IBinder service) { + mFuture.complete(INested.Stub.asInterface(service)); + } + + public void onServiceDisconnected(ComponentName className) { + mFuture = new CompletableFuture<>(); + } + }; + + + @Override + public IBinder onBind(Intent intent) { + return mBinder; + } + + private final IProtected.Stub mBinder = new IProtected.Stub() { + @Override + @EnforcePermission(android.Manifest.permission.INTERNET) + public void ProtectedByInternet() { + ProtectedByInternet_enforcePermission(); + } + + @Override + @EnforcePermission(android.Manifest.permission.VIBRATE) + public void ProtectedByVibrate() { + ProtectedByVibrate_enforcePermission(); + } + + @Override + @EnforcePermission(android.Manifest.permission.INTERNET) + public void ProtectedByInternetAndVibrateImplicitly() { + ProtectedByInternetAndVibrateImplicitly_enforcePermission(); + + ProtectedByVibrate(); + } + + @Override + @EnforcePermission(android.Manifest.permission.INTERNET) + public void ProtectedByInternetAndAccessNetworkStateImplicitly() throws RemoteException { + ProtectedByInternetAndAccessNetworkStateImplicitly_enforcePermission(); + + mNestedServiceConnection.get().ProtectedByAccessNetworkState(); + + } + + @Override + @EnforcePermission(android.Manifest.permission.INTERNET) + public void ProtectedByInternetAndReadSyncSettingsImplicitly() throws RemoteException { + ProtectedByInternetAndReadSyncSettingsImplicitly_enforcePermission(); + + mNestedServiceConnection.get().ProtectedByReadSyncSettings(); + } + }; +} diff --git a/tests/EnforcePermission/test-app/Android.bp b/tests/EnforcePermission/test-app/Android.bp new file mode 100644 index 000000000000..cd53854189b7 --- /dev/null +++ b/tests/EnforcePermission/test-app/Android.bp @@ -0,0 +1,38 @@ +// Copyright (C) 2023 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 { + default_applicable_licenses: ["frameworks_base_license"], +} + +android_test { + name: "EnforcePermissionTests", + srcs: [ + "src/**/*.java", + ":frameworks-enforce-permission-test-aidl", + ], + static_libs: [ + "androidx.test.rules", + ], + libs: [ + "android.test.base", + "android.test.runner", + ], + data: [ + ":EnforcePermissionTestHelper", + ], + platform_apis: true, + certificate: "platform", + test_suites: ["device-tests"], +} diff --git a/tests/EnforcePermission/test-app/AndroidManifest.xml b/tests/EnforcePermission/test-app/AndroidManifest.xml new file mode 100644 index 000000000000..4a0c6a86628f --- /dev/null +++ b/tests/EnforcePermission/test-app/AndroidManifest.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2023 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.tests.enforcepermission.tests"> + + <!-- Expected for the tests (not actually used) --> + <uses-permission android:name="android.permission.INTERNET" /> + <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" /> + + <queries> + <package android:name="android.tests.enforcepermission.service" /> + </queries> + + <application> + <uses-library android:name="android.test.runner" /> + </application> + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="android.tests.enforcepermission.tests"/> +</manifest> diff --git a/tests/EnforcePermission/test-app/AndroidTest.xml b/tests/EnforcePermission/test-app/AndroidTest.xml new file mode 100644 index 000000000000..120381a7fb83 --- /dev/null +++ b/tests/EnforcePermission/test-app/AndroidTest.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2023 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. +--> +<configuration description="Runs EnforcePermission End-to-End Tests"> + <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> + <option name="test-file-name" value="EnforcePermissionTestHelper.apk"/> + <option name="test-file-name" value="EnforcePermissionTests.apk"/> + <option name="cleanup-apks" value="true" /> + </target_preparer> + + <option name="test-tag" value="EnforcePermissionTests"/> + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="android.tests.enforcepermission.tests"/> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner"/> + </test> +</configuration> diff --git a/tests/EnforcePermission/test-app/src/android/tests/enforcepermission/tests/ServiceTest.java b/tests/EnforcePermission/test-app/src/android/tests/enforcepermission/tests/ServiceTest.java new file mode 100644 index 000000000000..d2a4a037f125 --- /dev/null +++ b/tests/EnforcePermission/test-app/src/android/tests/enforcepermission/tests/ServiceTest.java @@ -0,0 +1,129 @@ +/** + * Copyright (C) 2023 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.tests.enforcepermission.tests; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.os.RemoteException; +import android.tests.enforcepermission.IProtected; +import android.util.Log; + +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +@RunWith(AndroidJUnit4.class) +public class ServiceTest { + + private static final String TAG = "EnforcePermission.Tests"; + private static final String SERVICE_NAME = "android.tests.enforcepermission.service"; + private static final int SERVICE_TIMEOUT_SEC = 5; + + private Context mContext; + private volatile ServiceConnection mServiceConnection; + + @Before + public void bindTestService() throws Exception { + Log.d(TAG, "bindTestService"); + mContext = InstrumentationRegistry.getTargetContext(); + mServiceConnection = new ServiceConnection(); + Intent intent = new Intent(); + intent.setClassName(SERVICE_NAME, SERVICE_NAME + ".TestService"); + assertTrue(mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE)); + } + + @After + public void unbindTestService() throws Exception { + mContext.unbindService(mServiceConnection); + } + + private static final class ServiceConnection implements android.content.ServiceConnection { + private volatile CompletableFuture<IProtected> mFuture = new CompletableFuture<>(); + + @Override + public void onServiceConnected(ComponentName className, IBinder service) { + mFuture.complete(IProtected.Stub.asInterface(service)); + } + + @Override + public void onServiceDisconnected(ComponentName className) { + mFuture = new CompletableFuture<>(); + } + + public IProtected get() { + try { + return mFuture.get(SERVICE_TIMEOUT_SEC, TimeUnit.SECONDS); + } catch (ExecutionException | InterruptedException | TimeoutException e) { + throw new RuntimeException("Unable to reach TestService: " + e.toString()); + } + } + } + + @Test + public void testImmediatePermissionGranted_succeeds() + throws RemoteException { + mServiceConnection.get().ProtectedByInternet(); + } + + @Test + public void testImmediatePermissionNotGranted_fails() + throws RemoteException { + final Exception ex = assertThrows(SecurityException.class, + () -> mServiceConnection.get().ProtectedByVibrate()); + assertThat(ex.getMessage(), containsString("VIBRATE")); + } + + @Test + public void testImmediatePermissionGrantedButImplicitLocalNotGranted_fails() + throws RemoteException { + final Exception ex = assertThrows(SecurityException.class, + () -> mServiceConnection.get().ProtectedByInternetAndVibrateImplicitly()); + assertThat(ex.getMessage(), containsString("VIBRATE")); + } + + @Test + public void testImmediatePermissionGrantedButImplicitNestedNotGranted_fails() + throws RemoteException { + final Exception ex = assertThrows(SecurityException.class, + () -> mServiceConnection.get() + .ProtectedByInternetAndAccessNetworkStateImplicitly()); + assertThat(ex.getMessage(), containsString("ACCESS_NETWORK_STATE")); + } + + @Test + public void testImmediatePermissionGrantedAndImplicitNestedGranted_succeeds() + throws RemoteException { + mServiceConnection.get().ProtectedByInternetAndReadSyncSettingsImplicitly(); + } +} diff --git a/tests/FixVibrateSetting/src/com/android/fixvibratesetting/FixVibrateSetting.java b/tests/FixVibrateSetting/src/com/android/fixvibratesetting/FixVibrateSetting.java index 2e515705a253..761efe4a8484 100644 --- a/tests/FixVibrateSetting/src/com/android/fixvibratesetting/FixVibrateSetting.java +++ b/tests/FixVibrateSetting/src/com/android/fixvibratesetting/FixVibrateSetting.java @@ -110,7 +110,7 @@ public class FixVibrateSetting extends Activity implements View.OnClickListener private void test() { Intent intent = new Intent(this, FixVibrateSetting.class); - PendingIntent pending = PendingIntent.getActivity(this, 0, intent, 0); + PendingIntent pending = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE); Notification n = new Notification.Builder(this) .setSmallIcon(R.drawable.stat_sys_warning) diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp index 7731e098d9f5..5301d674bacf 100644 --- a/tests/FlickerTests/Android.bp +++ b/tests/FlickerTests/Android.bp @@ -47,6 +47,9 @@ android_test { "launcher-aosp-tapl", "platform-test-annotations", ], + data: [ + ":FlickerTestApp", + ], } java_library { diff --git a/tests/HandwritingIme/OWNERS b/tests/HandwritingIme/OWNERS new file mode 100644 index 000000000000..6bb4b17ed4eb --- /dev/null +++ b/tests/HandwritingIme/OWNERS @@ -0,0 +1,3 @@ +# Bug component: 34867 + +include /services/core/java/com/android/server/inputmethod/OWNERS diff --git a/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/ViewDumpParser.java b/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/ViewDumpParser.java index 2ad0da98c409..8b9c02049351 100644 --- a/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/ViewDumpParser.java +++ b/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/ViewDumpParser.java @@ -58,7 +58,7 @@ public class ViewDumpParser { Object hash = getProperty(props, "__hash__"); if (name instanceof String && hash instanceof Integer) { - return String.format(Locale.US, "%s@%x", name, hash); + return String.format(Locale.US, "%s@%x", name, (Integer) hash); } else { return null; } diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp index de9bbb6ef9fa..a92a6ae5a7a4 100644 --- a/tests/Input/Android.bp +++ b/tests/Input/Android.bp @@ -21,7 +21,7 @@ android_test { "services.core.unboosted", "testables", "truth-prebuilt", - "ub-uiautomator", + "androidx.test.uiautomator_uiautomator", ], test_suites: ["device-tests"], } diff --git a/tests/Input/src/com/android/test/input/AnrTest.kt b/tests/Input/src/com/android/test/input/AnrTest.kt index 1d65cc35c3bc..8025406499d8 100644 --- a/tests/Input/src/com/android/test/input/AnrTest.kt +++ b/tests/Input/src/com/android/test/input/AnrTest.kt @@ -27,14 +27,15 @@ import android.os.IInputConstants.UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLI import android.os.SystemClock import android.provider.Settings import android.provider.Settings.Global.HIDE_ERROR_DIALOGS -import android.support.test.uiautomator.By -import android.support.test.uiautomator.UiDevice -import android.support.test.uiautomator.UiObject2 -import android.support.test.uiautomator.Until import android.testing.PollingCheck import android.view.InputDevice import android.view.MotionEvent +import androidx.test.uiautomator.By +import androidx.test.uiautomator.UiDevice +import androidx.test.uiautomator.UiObject2 +import androidx.test.uiautomator.Until + import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue diff --git a/tests/Internal/src/com/android/internal/app/OWNERS b/tests/Internal/src/com/android/internal/app/OWNERS new file mode 100644 index 000000000000..d55dc78b8c0a --- /dev/null +++ b/tests/Internal/src/com/android/internal/app/OWNERS @@ -0,0 +1,2 @@ +# Locale related test +per-file *Locale* = file:/services/core/java/com/android/server/locales/OWNERS diff --git a/tests/JankBench/app/src/main/java/com/android/benchmark/app/HomeActivity.java b/tests/JankBench/app/src/main/java/com/android/benchmark/app/HomeActivity.java index 4de51fb57308..43dc9de6c90a 100644 --- a/tests/JankBench/app/src/main/java/com/android/benchmark/app/HomeActivity.java +++ b/tests/JankBench/app/src/main/java/com/android/benchmark/app/HomeActivity.java @@ -140,9 +140,9 @@ public class HomeActivity extends AppCompatActivity implements Button.OnClickLis handleNextBenchmark(); } + @SuppressWarnings("MissingSuperCall") // TODO: Fix me @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { - } private void handleNextBenchmark() { diff --git a/tests/JankBench/app/src/main/java/com/android/benchmark/app/RunLocalBenchmarksActivity.java b/tests/JankBench/app/src/main/java/com/android/benchmark/app/RunLocalBenchmarksActivity.java index c16efbda1830..d015a5695ec0 100644 --- a/tests/JankBench/app/src/main/java/com/android/benchmark/app/RunLocalBenchmarksActivity.java +++ b/tests/JankBench/app/src/main/java/com/android/benchmark/app/RunLocalBenchmarksActivity.java @@ -367,6 +367,7 @@ public class RunLocalBenchmarksActivity extends AppCompatActivity { } } + @SuppressWarnings("MissingSuperCall") // TODO: Fix me @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { diff --git a/tests/MirrorSurfaceTest/src/com/google/android/test/mirrorsurface/MirrorSurfaceActivity.java b/tests/MirrorSurfaceTest/src/com/google/android/test/mirrorsurface/MirrorSurfaceActivity.java index 8afe8411a790..17fa210a1db6 100644 --- a/tests/MirrorSurfaceTest/src/com/google/android/test/mirrorsurface/MirrorSurfaceActivity.java +++ b/tests/MirrorSurfaceTest/src/com/google/android/test/mirrorsurface/MirrorSurfaceActivity.java @@ -295,8 +295,8 @@ public class MirrorSurfaceActivity extends Activity implements View.OnClickListe private void updateMirror(Rect displayFrame, float scale) { if (displayFrame.isEmpty()) { Rect bounds = mWindowBounds; - int defaultCropW = Math.round(bounds.width() / 2); - int defaultCropH = Math.round(bounds.height() / 2); + int defaultCropW = bounds.width() / 2; + int defaultCropH = bounds.height() / 2; displayFrame.set(0, 0, defaultCropW, defaultCropH); } diff --git a/tests/OdmApps/Android.bp b/tests/OdmApps/Android.bp index de86498afd27..a5c6d6513f50 100644 --- a/tests/OdmApps/Android.bp +++ b/tests/OdmApps/Android.bp @@ -26,4 +26,8 @@ java_test_host { srcs: ["src/**/*.java"], libs: ["tradefed"], test_suites: ["device-tests"], + data: [ + ":TestOdmApp", + ":TestOdmPrivApp", + ], } diff --git a/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java b/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java index 241206d8919b..65b7549f22d1 100644 --- a/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java +++ b/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java @@ -24,18 +24,14 @@ public class MainActivity extends Activity implements OnItemClickListener { static final String KEY_NAME = "name"; static final String KEY_CLASS = "clazz"; - static Map<String,?> make(String name) { - Map<String,Object> ret = new HashMap<String,Object>(); - ret.put(KEY_NAME, name); - return ret; - } - - @SuppressWarnings("serial") - static final ArrayList<Map<String,?>> SAMPLES = new ArrayList<Map<String,?>>() {{ + static final ArrayList<Map<String, ?>> SAMPLES = new ArrayList<>(); + static { for (int i = 1; i < 25; i++) { - add(make("List Item: " + i)); + Map<String, Object> sample = new HashMap<String, Object>(); + sample.put(KEY_NAME, "List Item: " + i); + SAMPLES.add(sample); } - }}; + } Handler mHandler = new Handler(); diff --git a/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp index 9f6ce4e8425b..f2234fb64108 100644 --- a/tests/RollbackTest/Android.bp +++ b/tests/RollbackTest/Android.bp @@ -51,6 +51,7 @@ java_test_host { data: [ ":com.android.apex.apkrollback.test_v1", ":test.rebootless_apex_v1", + ":RollbackTest", ], } @@ -61,6 +62,7 @@ java_test_host { static_libs: ["RollbackTestLib", "frameworks-base-hostutils"], test_suites: ["general-tests"], test_config: "NetworkStagedRollbackTest.xml", + data: [":RollbackTest"], } java_test_host { diff --git a/tests/RollbackTest/SampleRollbackApp/Android.bp b/tests/RollbackTest/SampleRollbackApp/Android.bp new file mode 100644 index 000000000000..074c7bc39155 --- /dev/null +++ b/tests/RollbackTest/SampleRollbackApp/Android.bp @@ -0,0 +1,33 @@ +// Copyright (C) 2022 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 { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +android_app { + name: "SampleRollbackApp", + srcs: [ + "src/**/*.java", + ], + resource_dirs: ["res"], + certificate: "platform", + sdk_version: "system_current", + min_sdk_version: "29", +} diff --git a/tests/RollbackTest/SampleRollbackApp/AndroidManifest.xml b/tests/RollbackTest/SampleRollbackApp/AndroidManifest.xml new file mode 100644 index 000000000000..7fe4bae2a3fe --- /dev/null +++ b/tests/RollbackTest/SampleRollbackApp/AndroidManifest.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2022 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.sample.rollbackapp" > + <uses-permission android:name="android.permission.MANAGE_ROLLBACKS" /> + <application + android:label="@string/title_activity_main"> + <activity + android:name="com.android.sample.rollbackapp.MainActivity" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> +</manifest> diff --git a/tests/RollbackTest/SampleRollbackApp/res/layout/activity_main.xml b/tests/RollbackTest/SampleRollbackApp/res/layout/activity_main.xml new file mode 100644 index 000000000000..3fb987bb539c --- /dev/null +++ b/tests/RollbackTest/SampleRollbackApp/res/layout/activity_main.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2022 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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <Button + android:id="@+id/trigger_rollback_button" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + style="?android:attr/buttonBarButtonStyle" + android:text="Rollback Selected" /> + + <ListView + android:id="@+id/listView" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:divider="?android:attr/dividerHorizontal" + android:dividerHeight="1dp" /> + +</LinearLayout>
\ No newline at end of file diff --git a/tests/RollbackTest/SampleRollbackApp/res/layout/listitem_rollbackinfo.xml b/tests/RollbackTest/SampleRollbackApp/res/layout/listitem_rollbackinfo.xml new file mode 100644 index 000000000000..f650dd5d2230 --- /dev/null +++ b/tests/RollbackTest/SampleRollbackApp/res/layout/listitem_rollbackinfo.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2022 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" +> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:paddingTop="10dp" + android:paddingLeft="10dp" + android:paddingRight="10dp"> + <TextView android:id="@+id/rollback_id" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textSize="20dp"/> + <TextView android:id="@+id/rollback_packages" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textSize="16dp"/> + <CheckBox android:id="@+id/checkbox" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Roll Back"/> + </LinearLayout> +</RelativeLayout>
\ No newline at end of file diff --git a/tests/RollbackTest/SampleRollbackApp/res/values/strings.xml b/tests/RollbackTest/SampleRollbackApp/res/values/strings.xml new file mode 100644 index 000000000000..a85b6800a146 --- /dev/null +++ b/tests/RollbackTest/SampleRollbackApp/res/values/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2022 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<resources> + <string name="title_activity_main" description="Launcher title">Rollback Sample App</string> +</resources>
\ No newline at end of file diff --git a/tests/RollbackTest/SampleRollbackApp/src/com/android/sample/rollbackapp/MainActivity.java b/tests/RollbackTest/SampleRollbackApp/src/com/android/sample/rollbackapp/MainActivity.java new file mode 100644 index 000000000000..79a2f1f5f4de --- /dev/null +++ b/tests/RollbackTest/SampleRollbackApp/src/com/android/sample/rollbackapp/MainActivity.java @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2022 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.sample.rollbackapp; + +import static android.app.PendingIntent.FLAG_MUTABLE; + +import android.app.Activity; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.rollback.PackageRollbackInfo; +import android.content.rollback.RollbackInfo; +import android.content.rollback.RollbackManager; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.ListView; +import android.widget.TextView; +import android.widget.Toast; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class MainActivity extends Activity { + + List<Integer> mIdsToRollback = new ArrayList<>(); + Button mTriggerRollbackButton; + RollbackManager mRollbackManager; + static final String ROLLBACK_ID_EXTRA = "rollbackId"; + static final String ACTION_NAME = MainActivity.class.getName(); + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + ListView rollbackListView = findViewById(R.id.listView); + mRollbackManager = getApplicationContext().getSystemService(RollbackManager.class); + initTriggerRollbackButton(); + + // Populate list of available rollbacks. + List<RollbackInfo> availableRollbacks = mRollbackManager.getAvailableRollbacks(); + CustomAdapter adapter = new CustomAdapter(availableRollbacks); + rollbackListView.setAdapter(adapter); + + // Register receiver for rollback status events. + getApplicationContext().registerReceiver( + new BroadcastReceiver() { + @Override + public void onReceive(Context context, + Intent intent) { + int rollbackId = intent.getIntExtra(ROLLBACK_ID_EXTRA, -1); + int rollbackStatusCode = intent.getIntExtra(RollbackManager.EXTRA_STATUS, + RollbackManager.STATUS_FAILURE); + String rollbackStatus = "FAILED"; + if (rollbackStatusCode == RollbackManager.STATUS_SUCCESS) { + rollbackStatus = "SUCCESS"; + mTriggerRollbackButton.setClickable(false); + } + makeToast("Status for rollback ID " + rollbackId + " is " + rollbackStatus); + }}, new IntentFilter(ACTION_NAME), Context.RECEIVER_NOT_EXPORTED); + } + + private void initTriggerRollbackButton() { + mTriggerRollbackButton = findViewById(R.id.trigger_rollback_button); + mTriggerRollbackButton.setClickable(false); + mTriggerRollbackButton.setOnClickListener(v -> { + // Commits all selected rollbacks. Rollback status events will be sent to our receiver. + for (int i = 0; i < mIdsToRollback.size(); i++) { + Intent intent = new Intent(ACTION_NAME); + intent.putExtra(ROLLBACK_ID_EXTRA, mIdsToRollback.get(i)); + PendingIntent pendingIntent = PendingIntent.getBroadcast( + getApplicationContext(), 0, intent, FLAG_MUTABLE); + mRollbackManager.commitRollback(mIdsToRollback.get(i), + Collections.emptyList(), + pendingIntent.getIntentSender()); + } + }); + } + + + + private void makeToast(String message) { + runOnUiThread(() -> Toast.makeText(MainActivity.this, message, Toast.LENGTH_LONG).show()); + } + + public class CustomAdapter extends BaseAdapter { + List<RollbackInfo> mRollbackInfos; + LayoutInflater mInflater = LayoutInflater.from(getApplicationContext()); + + CustomAdapter(List<RollbackInfo> rollbackInfos) { + mRollbackInfos = rollbackInfos; + } + + @Override + public int getCount() { + return mRollbackInfos.size(); + } + + @Override + public Object getItem(int position) { + return mRollbackInfos.get(position); + } + + @Override + public long getItemId(int position) { + return mRollbackInfos.get(position).getRollbackId(); + } + + @Override + public View getView(int position, View view, ViewGroup parent) { + if (view == null) { + view = mInflater.inflate(R.layout.listitem_rollbackinfo, null); + } + RollbackInfo rollbackInfo = mRollbackInfos.get(position); + TextView rollbackIdView = view.findViewById(R.id.rollback_id); + rollbackIdView.setText("Rollback ID " + rollbackInfo.getRollbackId()); + TextView rollbackPackagesTextView = view.findViewById(R.id.rollback_packages); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < rollbackInfo.getPackages().size(); i++) { + PackageRollbackInfo pkgInfo = rollbackInfo.getPackages().get(i); + sb.append(pkgInfo.getPackageName() + ": " + + pkgInfo.getVersionRolledBackFrom().getLongVersionCode() + " -> " + + pkgInfo.getVersionRolledBackTo().getLongVersionCode() + ","); + } + sb.deleteCharAt(sb.length() - 1); + rollbackPackagesTextView.setText(sb.toString()); + CheckBox checkbox = view.findViewById(R.id.checkbox); + checkbox.setOnCheckedChangeListener((buttonView, isChecked) -> { + if (isChecked) { + mIdsToRollback.add(rollbackInfo.getRollbackId()); + } else { + mIdsToRollback.remove(Integer.valueOf(rollbackInfo.getRollbackId())); + } + mTriggerRollbackButton.setClickable(mIdsToRollback.size() > 0); + }); + return view; + } + } +} diff --git a/tests/SmokeTestApps/src/com/android/smoketest/triggers/CrashyApp.java b/tests/SmokeTestApps/src/com/android/smoketest/triggers/CrashyApp.java index c11b0f3acf79..f85fb0f267d5 100644 --- a/tests/SmokeTestApps/src/com/android/smoketest/triggers/CrashyApp.java +++ b/tests/SmokeTestApps/src/com/android/smoketest/triggers/CrashyApp.java @@ -30,6 +30,7 @@ public class CrashyApp extends Activity { setContentView(tv); } + @SuppressWarnings("ReturnValueIgnored") @Override public void onResume() { ((String) null).length(); diff --git a/tests/SoundTriggerTestApp/OWNERS b/tests/SoundTriggerTestApp/OWNERS index 9db19a37812b..a0fcfc52704d 100644 --- a/tests/SoundTriggerTestApp/OWNERS +++ b/tests/SoundTriggerTestApp/OWNERS @@ -1,2 +1,2 @@ -include /core/java/android/media/soundtrigger/OWNERS +include /media/java/android/media/soundtrigger/OWNERS mdooley@google.com diff --git a/tests/SoundTriggerTests/OWNERS b/tests/SoundTriggerTests/OWNERS index 816bc6bba639..1e41886fe716 100644 --- a/tests/SoundTriggerTests/OWNERS +++ b/tests/SoundTriggerTests/OWNERS @@ -1 +1 @@ -include /core/java/android/media/soundtrigger/OWNERS +include /media/java/android/media/soundtrigger/OWNERS diff --git a/tests/StagedInstallTest/Android.bp b/tests/StagedInstallTest/Android.bp index cce0dde9e6b9..38e74470e3d5 100644 --- a/tests/StagedInstallTest/Android.bp +++ b/tests/StagedInstallTest/Android.bp @@ -55,6 +55,7 @@ java_test_host { "cts-install-lib-host", ], data: [ + ":StagedInstallInternalTestApp", ":apex.apexd_test", ":com.android.apex.apkrollback.test_v1", ":StagedInstallTestApexV2", diff --git a/tests/SurfaceControlViewHostTest/OWNERS b/tests/SurfaceControlViewHostTest/OWNERS new file mode 100644 index 000000000000..0862c05e0ee4 --- /dev/null +++ b/tests/SurfaceControlViewHostTest/OWNERS @@ -0,0 +1 @@ +include /services/core/java/com/android/server/wm/OWNERS diff --git a/tests/SystemMemoryTest/host/Android.bp b/tests/SystemMemoryTest/host/Android.bp index 79744625b752..cc8bc45a7411 100644 --- a/tests/SystemMemoryTest/host/Android.bp +++ b/tests/SystemMemoryTest/host/Android.bp @@ -26,4 +26,7 @@ java_test_host { srcs: ["src/**/*.java"], libs: ["tradefed"], test_suites: ["general-tests"], + data: [ + ":SystemMemoryTestDevice", + ], } diff --git a/tests/TouchLatency/OWNERS b/tests/TouchLatency/OWNERS new file mode 100644 index 000000000000..2b7de2555587 --- /dev/null +++ b/tests/TouchLatency/OWNERS @@ -0,0 +1,2 @@ +include platform/frameworks/base:/graphics/java/android/graphics/OWNERS +include platform/frameworks/native:/services/surfaceflinger/OWNERS
\ No newline at end of file diff --git a/tests/TrustTests/Android.bp b/tests/TrustTests/Android.bp index 77f98e88f1eb..a1b888aef934 100644 --- a/tests/TrustTests/Android.bp +++ b/tests/TrustTests/Android.bp @@ -24,7 +24,7 @@ android_test { static_libs: [ "androidx.test.rules", "androidx.test.ext.junit", - "androidx.test.uiautomator", + "androidx.test.uiautomator_uiautomator", "mockito-target-minus-junit4", "servicestests-utils", "truth-prebuilt", diff --git a/tests/UpdatableSystemFontTest/Android.bp b/tests/UpdatableSystemFontTest/Android.bp index 9a9e42bfc300..9bfcc18ee301 100644 --- a/tests/UpdatableSystemFontTest/Android.bp +++ b/tests/UpdatableSystemFontTest/Android.bp @@ -37,6 +37,7 @@ android_test { "vts", ], data: [ + ":EmojiRenderingTestApp", ":UpdatableSystemFontTestCertDer", ":UpdatableSystemFontTest_NotoColorEmoji.ttf", ":UpdatableSystemFontTest_NotoColorEmoji.sig", diff --git a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java index cbe13d9aa149..650686ff3b85 100644 --- a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java +++ b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java @@ -373,6 +373,10 @@ public class UpdatableSystemFontTest { try (InputStream is = new FileInputStream(certPath)) { result = runShellCommand("mini-keyctl padd asymmetric fsv_test .fs-verity", is); } + // /data/local/tmp is not readable by system server. Copy a cert file to /data/fonts + final String copiedCert = "/data/fonts/debug_cert.der"; + runShellCommand("cp " + certPath + " " + copiedCert, null); + runShellCommand("cmd font install-debug-cert " + copiedCert, null); // Assert that there are no errors. assertThat(result.second).isEmpty(); String keyId = result.first.trim(); diff --git a/tests/UsbManagerTests/lib/src/com/android/server/usblib/UsbManagerTestLib.java b/tests/UsbManagerTests/lib/src/com/android/server/usblib/UsbManagerTestLib.java index 782439f80fc8..98bf541944c3 100644 --- a/tests/UsbManagerTests/lib/src/com/android/server/usblib/UsbManagerTestLib.java +++ b/tests/UsbManagerTests/lib/src/com/android/server/usblib/UsbManagerTestLib.java @@ -24,12 +24,15 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.hardware.usb.UsbManager; +import android.os.Binder; import android.os.RemoteException; import android.util.Log; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.concurrent.atomic.AtomicInteger; + /** * Unit tests lib for {@link android.hardware.usb.UsbManager}. */ @@ -42,6 +45,11 @@ public class UsbManagerTestLib { private UsbManager mUsbManagerMock; @Mock private android.hardware.usb.IUsbManager mMockUsbService; + /** + * Counter for tracking UsbOperation operations. + */ + private static final AtomicInteger sUsbOperationCount = new AtomicInteger(); + public UsbManagerTestLib(Context context) { MockitoAnnotations.initMocks(this); mContext = context; @@ -82,10 +90,11 @@ public class UsbManagerTestLib { } private void testSetCurrentFunctionsMock_Matched(long functions) { + int operationId = sUsbOperationCount.incrementAndGet() + Binder.getCallingUid(); try { setCurrentFunctions(functions); - verify(mMockUsbService).setCurrentFunctions(eq(functions)); + verify(mMockUsbService).setCurrentFunctions(eq(functions), operationId); } catch (RemoteException remEx) { Log.w(TAG, "RemoteException"); } @@ -106,9 +115,10 @@ public class UsbManagerTestLib { } public void testSetCurrentFunctionsEx(long functions) throws Exception { + int operationId = sUsbOperationCount.incrementAndGet() + Binder.getCallingUid(); setCurrentFunctions(functions); - verify(mMockUsbService).setCurrentFunctions(eq(functions)); + verify(mMockUsbService).setCurrentFunctions(eq(functions), operationId); } public void testGetCurrentFunctions_shouldMatched() { diff --git a/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java b/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java index 861d221238ff..e06a4cb92609 100644 --- a/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java +++ b/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java @@ -98,7 +98,7 @@ public class UsbHandlerTest { } @Override - protected void setEnabledFunctions(long functions, boolean force) { + protected void setEnabledFunctions(long functions, boolean force, int operationId) { mCurrentFunctions = functions; } @@ -134,6 +134,20 @@ public class UsbHandlerTest { protected void sendStickyBroadcast(Intent intent) { mBroadcastedIntent = intent; } + + @Override + public void handlerInitDone(int operationId) { + } + + @Override + public void setCurrentUsbFunctionsCb(long functions, + int status, int mRequest, long mFunctions, boolean mChargingFunctions){ + } + + @Override + public void getUsbSpeedCb(int speed){ + } + } @Before diff --git a/tests/VectorDrawableTest/OWNERS b/tests/VectorDrawableTest/OWNERS new file mode 100644 index 000000000000..27e16681899e --- /dev/null +++ b/tests/VectorDrawableTest/OWNERS @@ -0,0 +1,3 @@ +# Bug component: 24939 + +include /graphics/java/android/graphics/OWNERS diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java index db4898492ac5..661dd845757b 100644 --- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java +++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java @@ -33,6 +33,8 @@ import android.widget.CheckBox; import android.widget.ImageView; import android.widget.TextView; +import java.util.Arrays; + public class MainInteractionSession extends VoiceInteractionSession implements View.OnClickListener { static final String TAG = "MainInteractionSession"; @@ -403,7 +405,7 @@ public class MainInteractionSession extends VoiceInteractionSession @Override public void onRequestPickOption(PickOptionRequest request) { Log.i(TAG, "onPickOption: prompt=" + request.getVoicePrompt() + " options=" - + request.getOptions() + " extras=" + request.getExtras()); + + Arrays.toString(request.getOptions()) + " extras=" + request.getExtras()); mConfirmButton.setText("Pick Option"); mPendingRequest = request; setPrompt(request.getVoicePrompt()); diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/StartVoiceInteractionActivity.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/StartVoiceInteractionActivity.java index 733f602f6c14..8ae7186461d3 100644 --- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/StartVoiceInteractionActivity.java +++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/StartVoiceInteractionActivity.java @@ -24,6 +24,8 @@ import android.view.View; import android.widget.Button; import android.widget.TextView; +import java.util.Arrays; + public class StartVoiceInteractionActivity extends Activity implements View.OnClickListener { static final String TAG = "LocalVoiceInteractionActivity"; @@ -187,7 +189,8 @@ public class StartVoiceInteractionActivity extends Activity implements View.OnCl } @Override public void onPickOptionResult(boolean finished, Option[] selections, Bundle result) { - Log.i(TAG, "Pick result: finished=" + finished + " selections=" + selections + Log.i(TAG, "Pick result: finished=" + finished + + " selections=" + Arrays.toString(selections) + " result=" + result); StringBuilder sb = new StringBuilder(); if (finished) { diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java index ada0e21e059a..4fc3a15ac38d 100644 --- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java +++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java @@ -28,6 +28,8 @@ import android.view.View; import android.widget.Button; import android.widget.TextView; +import java.util.Arrays; + public class TestInteractionActivity extends Activity implements View.OnClickListener { static final String TAG = "TestInteractionActivity"; @@ -240,7 +242,8 @@ public class TestInteractionActivity extends Activity implements View.OnClickLis } @Override public void onPickOptionResult(boolean finished, Option[] selections, Bundle result) { - Log.i(TAG, "Pick result: finished=" + finished + " selections=" + selections + Log.i(TAG, "Pick result: finished=" + finished + + " selections=" + Arrays.toString(selections) + " result=" + result); StringBuilder sb = new StringBuilder(); if (finished) { diff --git a/tests/WindowAnimationJank/Android.bp b/tests/WindowAnimationJank/Android.bp index ed86aa5f90ea..8542f885d645 100644 --- a/tests/WindowAnimationJank/Android.bp +++ b/tests/WindowAnimationJank/Android.bp @@ -25,7 +25,7 @@ android_test { name: "WindowAnimationJank", srcs: ["src/**/*.java"], static_libs: [ - "ub-uiautomator", + "androidx.test.uiautomator_uiautomator", "androidx.test.janktesthelper", "junit", ], diff --git a/tests/WindowAnimationJank/src/android/windowanimationjank/Utils.java b/tests/WindowAnimationJank/src/android/windowanimationjank/Utils.java index 25314644ca7e..48a359c4d0c6 100644 --- a/tests/WindowAnimationJank/src/android/windowanimationjank/Utils.java +++ b/tests/WindowAnimationJank/src/android/windowanimationjank/Utils.java @@ -18,11 +18,12 @@ import android.app.UiAutomation; import android.content.ComponentName; import android.content.Intent; import android.os.SystemClock; -import android.support.test.uiautomator.By; -import android.support.test.uiautomator.BySelector; -import android.support.test.uiautomator.UiDevice; -import android.support.test.uiautomator.UiObject2; -import android.support.test.uiautomator.Until; + +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.BySelector; +import androidx.test.uiautomator.UiDevice; +import androidx.test.uiautomator.UiObject2; +import androidx.test.uiautomator.Until; /** * Set of helpers to manipulate test activities. diff --git a/tests/WindowAnimationJank/src/android/windowanimationjank/WindowAnimationJankTestBase.java b/tests/WindowAnimationJank/src/android/windowanimationjank/WindowAnimationJankTestBase.java index a8ace162c4d0..cb7c5112cba7 100644 --- a/tests/WindowAnimationJank/src/android/windowanimationjank/WindowAnimationJankTestBase.java +++ b/tests/WindowAnimationJank/src/android/windowanimationjank/WindowAnimationJankTestBase.java @@ -16,9 +16,8 @@ package android.windowanimationjank; -import android.support.test.uiautomator.UiDevice; - import androidx.test.jank.JankTestBase; +import androidx.test.uiautomator.UiDevice; /** * This adds additional system level jank monitor and its result is merged with primary monitor diff --git a/tests/utils/testutils/java/android/os/test/FakePermissionEnforcer.java b/tests/utils/testutils/java/android/os/test/FakePermissionEnforcer.java new file mode 100644 index 000000000000..b94bb41c0988 --- /dev/null +++ b/tests/utils/testutils/java/android/os/test/FakePermissionEnforcer.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2023 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.test; + +import static android.permission.PermissionManager.PERMISSION_GRANTED; +import static android.permission.PermissionManager.PERMISSION_HARD_DENIED; + +import android.annotation.NonNull; +import android.content.AttributionSource; +import android.os.PermissionEnforcer; + +import java.util.HashSet; +import java.util.Set; + +/** + * Fake for {@link PermissionEnforcer}. Useful for tests wanting to mock the + * permission checks of an AIDL service. FakePermissionEnforcer may be passed + * to the constructor of the AIDL-generated Stub class. + * + */ +public class FakePermissionEnforcer extends PermissionEnforcer { + private Set<String> mGranted; + + public FakePermissionEnforcer() { + mGranted = new HashSet(); + } + + public void grant(String permission) { + mGranted.add(permission); + } + + public void revoke(String permission) { + mGranted.remove(permission); + } + + private boolean granted(String permission) { + return mGranted.contains(permission); + } + + @Override + protected int checkPermission(@NonNull String permission, + @NonNull AttributionSource source) { + return granted(permission) ? PERMISSION_GRANTED : PERMISSION_HARD_DENIED; + } + + @Override + protected int checkPermission(@NonNull String permission, int pid, int uid) { + return granted(permission) ? PERMISSION_GRANTED : PERMISSION_HARD_DENIED; + } +} diff --git a/tests/utils/testutils/java/android/os/test/OWNERS b/tests/utils/testutils/java/android/os/test/OWNERS new file mode 100644 index 000000000000..3a9129e1bb69 --- /dev/null +++ b/tests/utils/testutils/java/android/os/test/OWNERS @@ -0,0 +1 @@ +per-file FakePermissionEnforcer.java = file:/tests/EnforcePermission/OWNERS diff --git a/tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java b/tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java index 3da8b460df13..cc3781a0bfb3 100644 --- a/tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java +++ b/tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java @@ -147,12 +147,39 @@ public class BroadcastInterceptingContext extends ContextWrapper { @Override public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) { - return registerReceiver(receiver, filter, null, null); + return registerReceiver(receiver, filter, null, null, 0); + } + + /** + * Registers the specified {@code receiver} to listen for broadcasts that match the {@code + * filter} in the current process. + * + * <p>Since this method only listens for broadcasts in the current process, the provided {@code + * flags} are ignored; this method is primarily intended to allow receivers that register with + * flags to register in the current process during tests. + */ + @Override + public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, int flags) { + return registerReceiver(receiver, filter, null, null, flags); } @Override public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler) { + return registerReceiver(receiver, filter, broadcastPermission, scheduler, 0); + } + + /** + * Registers the specified {@code receiver} to listen for broadcasts that match the {@code + * filter} to run in the context of the specified {@code scheduler} in the current process. + * + * <p>Since this method only listens for broadcasts in the current process, the provided {@code + * flags} are ignored; this method is primarily intended to allow receivers that register with + * flags to register in the current process during tests. + */ + @Override + public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, + String broadcastPermission, Handler scheduler, int flags) { synchronized (mInterceptors) { mInterceptors.add(new BroadcastInterceptor(receiver, filter, scheduler)); } @@ -219,6 +246,12 @@ public class BroadcastInterceptingContext extends ContextWrapper { } @Override + public void sendBroadcastAsUser(Intent intent, UserHandle user, + String receiverPermission, Bundle options) { + sendBroadcast(intent); + } + + @Override public void sendStickyBroadcast(Intent intent) { sendBroadcast(intent); } diff --git a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java index 2fbcf9d87bd4..156961312323 100644 --- a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java +++ b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java @@ -20,6 +20,7 @@ import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN; import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.fail; import org.junit.Test; @@ -31,8 +32,8 @@ public class VcnCellUnderlyingNetworkTemplateTest extends VcnUnderlyingNetworkTe private static final Set<String> ALLOWED_PLMN_IDS = new HashSet<>(); private static final Set<Integer> ALLOWED_CARRIER_IDS = new HashSet<>(); - // Package private for use in VcnGatewayConnectionConfigTest - static VcnCellUnderlyingNetworkTemplate getTestNetworkTemplate() { + // Public for use in UnderlyingNetworkControllerTest + public static VcnCellUnderlyingNetworkTemplate.Builder getTestNetworkTemplateBuilder() { return new VcnCellUnderlyingNetworkTemplate.Builder() .setMetered(MATCH_FORBIDDEN) .setMinUpstreamBandwidthKbps( @@ -44,13 +45,44 @@ public class VcnCellUnderlyingNetworkTemplateTest extends VcnUnderlyingNetworkTe .setOperatorPlmnIds(ALLOWED_PLMN_IDS) .setSimSpecificCarrierIds(ALLOWED_CARRIER_IDS) .setRoaming(MATCH_FORBIDDEN) - .setOpportunistic(MATCH_REQUIRED) - .build(); + .setOpportunistic(MATCH_REQUIRED); + } + + // Package private for use in VcnGatewayConnectionConfigTest + static VcnCellUnderlyingNetworkTemplate getTestNetworkTemplate() { + return getTestNetworkTemplateBuilder().build(); + } + + private void setAllCapabilities( + VcnCellUnderlyingNetworkTemplate.Builder builder, int matchCriteria) { + builder.setCbs(matchCriteria); + builder.setDun(matchCriteria); + builder.setIms(matchCriteria); + builder.setInternet(matchCriteria); + builder.setMms(matchCriteria); + builder.setRcs(matchCriteria); + } + + private void verifyAllCapabilities( + VcnCellUnderlyingNetworkTemplate template, + int expectMatchCriteriaforNonInternet, + int expectMatchCriteriaforInternet) { + assertEquals(expectMatchCriteriaforNonInternet, template.getCbs()); + assertEquals(expectMatchCriteriaforNonInternet, template.getDun()); + assertEquals(expectMatchCriteriaforNonInternet, template.getIms()); + assertEquals(expectMatchCriteriaforNonInternet, template.getMms()); + assertEquals(expectMatchCriteriaforNonInternet, template.getRcs()); + + assertEquals(expectMatchCriteriaforInternet, template.getInternet()); } @Test public void testBuilderAndGetters() { - final VcnCellUnderlyingNetworkTemplate networkPriority = getTestNetworkTemplate(); + final VcnCellUnderlyingNetworkTemplate.Builder builder = getTestNetworkTemplateBuilder(); + setAllCapabilities(builder, MATCH_REQUIRED); + + final VcnCellUnderlyingNetworkTemplate networkPriority = builder.build(); + assertEquals(MATCH_FORBIDDEN, networkPriority.getMetered()); assertEquals( TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS, @@ -68,6 +100,8 @@ public class VcnCellUnderlyingNetworkTemplateTest extends VcnUnderlyingNetworkTe assertEquals(ALLOWED_CARRIER_IDS, networkPriority.getSimSpecificCarrierIds()); assertEquals(MATCH_FORBIDDEN, networkPriority.getRoaming()); assertEquals(MATCH_REQUIRED, networkPriority.getOpportunistic()); + + verifyAllCapabilities(networkPriority, MATCH_REQUIRED, MATCH_REQUIRED); } @Test @@ -86,6 +120,8 @@ public class VcnCellUnderlyingNetworkTemplateTest extends VcnUnderlyingNetworkTe assertEquals(new HashSet<Integer>(), networkPriority.getSimSpecificCarrierIds()); assertEquals(MATCH_ANY, networkPriority.getRoaming()); assertEquals(MATCH_ANY, networkPriority.getOpportunistic()); + + verifyAllCapabilities(networkPriority, MATCH_ANY, MATCH_REQUIRED); } @Test @@ -112,6 +148,25 @@ public class VcnCellUnderlyingNetworkTemplateTest extends VcnUnderlyingNetworkTe } @Test + public void testBuildFailWithoutRequiredCapabilities() { + try { + new VcnCellUnderlyingNetworkTemplate.Builder().setInternet(MATCH_ANY).build(); + + fail("Expected IAE for missing required capabilities"); + } catch (IllegalArgumentException expected) { + } + } + + @Test + public void testEqualsWithDifferentCapabilities() { + final VcnCellUnderlyingNetworkTemplate left = + new VcnCellUnderlyingNetworkTemplate.Builder().setDun(MATCH_REQUIRED).build(); + final VcnCellUnderlyingNetworkTemplate right = + new VcnCellUnderlyingNetworkTemplate.Builder().setMms(MATCH_REQUIRED).build(); + assertNotEquals(left, right); + } + + @Test public void testPersistableBundle() { final VcnCellUnderlyingNetworkTemplate networkPriority = getTestNetworkTemplate(); assertEquals( @@ -119,4 +174,16 @@ public class VcnCellUnderlyingNetworkTemplateTest extends VcnUnderlyingNetworkTe VcnUnderlyingNetworkTemplate.fromPersistableBundle( networkPriority.toPersistableBundle())); } + + @Test + public void testPersistableBundleWithCapabilities() { + final VcnCellUnderlyingNetworkTemplate.Builder builder = getTestNetworkTemplateBuilder(); + setAllCapabilities(builder, MATCH_REQUIRED); + + final VcnCellUnderlyingNetworkTemplate networkPriority = builder.build(); + assertEquals( + networkPriority, + VcnUnderlyingNetworkTemplate.fromPersistableBundle( + networkPriority.toPersistableBundle())); + } } diff --git a/tests/vcn/java/android/net/vcn/VcnConfigTest.java b/tests/vcn/java/android/net/vcn/VcnConfigTest.java index 7ac51b7e3342..73a0a6183cb6 100644 --- a/tests/vcn/java/android/net/vcn/VcnConfigTest.java +++ b/tests/vcn/java/android/net/vcn/VcnConfigTest.java @@ -16,7 +16,13 @@ package android.net.vcn; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; +import static android.net.NetworkCapabilities.TRANSPORT_TEST; +import static android.net.NetworkCapabilities.TRANSPORT_WIFI; + import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.fail; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -24,6 +30,7 @@ import static org.mockito.Mockito.mock; import android.annotation.NonNull; import android.content.Context; import android.os.Parcel; +import android.util.ArraySet; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -42,19 +49,36 @@ public class VcnConfigTest { private static final Set<VcnGatewayConnectionConfig> GATEWAY_CONNECTION_CONFIGS = Collections.singleton(VcnGatewayConnectionConfigTest.buildTestConfig()); + private static final Set<Integer> RESTRICTED_TRANSPORTS = new ArraySet<>(); + + static { + RESTRICTED_TRANSPORTS.add(TRANSPORT_WIFI); + RESTRICTED_TRANSPORTS.add(TRANSPORT_CELLULAR); + } + private final Context mContext = mock(Context.class); // Public visibility for VcnManagementServiceTest - public static VcnConfig buildTestConfig(@NonNull Context context) { + public static VcnConfig buildTestConfig( + @NonNull Context context, Set<Integer> restrictedTransports) { VcnConfig.Builder builder = new VcnConfig.Builder(context); for (VcnGatewayConnectionConfig gatewayConnectionConfig : GATEWAY_CONNECTION_CONFIGS) { builder.addGatewayConnectionConfig(gatewayConnectionConfig); } + if (restrictedTransports != null) { + builder.setRestrictedUnderlyingNetworkTransports(restrictedTransports); + } + return builder.build(); } + // Public visibility for VcnManagementServiceTest + public static VcnConfig buildTestConfig(@NonNull Context context) { + return buildTestConfig(context, null); + } + @Before public void setUp() throws Exception { doReturn(TEST_PACKAGE_NAME).when(mContext).getOpPackageName(); @@ -91,11 +115,25 @@ public class VcnConfigTest { } @Test - public void testBuilderAndGetters() { + public void testBuilderAndGettersDefaultValues() { final VcnConfig config = buildTestConfig(mContext); assertEquals(TEST_PACKAGE_NAME, config.getProvisioningPackageName()); assertEquals(GATEWAY_CONNECTION_CONFIGS, config.getGatewayConnectionConfigs()); + assertFalse(config.isTestModeProfile()); + assertEquals( + Collections.singleton(TRANSPORT_WIFI), + config.getRestrictedUnderlyingNetworkTransports()); + } + + @Test + public void testBuilderAndGettersConfigRestrictedTransports() { + final VcnConfig config = buildTestConfig(mContext, RESTRICTED_TRANSPORTS); + + assertEquals(TEST_PACKAGE_NAME, config.getProvisioningPackageName()); + assertEquals(GATEWAY_CONNECTION_CONFIGS, config.getGatewayConnectionConfigs()); + assertFalse(config.isTestModeProfile()); + assertEquals(RESTRICTED_TRANSPORTS, config.getRestrictedUnderlyingNetworkTransports()); } @Test @@ -106,6 +144,55 @@ public class VcnConfigTest { } @Test + public void testPersistableBundleWithRestrictedTransports() { + final VcnConfig config = buildTestConfig(mContext, RESTRICTED_TRANSPORTS); + + assertEquals(config, new VcnConfig(config.toPersistableBundle())); + } + + @Test + public void testEqualityWithRestrictedTransports() { + final VcnConfig config = buildTestConfig(mContext, RESTRICTED_TRANSPORTS); + final VcnConfig configEqual = buildTestConfig(mContext, RESTRICTED_TRANSPORTS); + final VcnConfig configNotEqual = + buildTestConfig(mContext, Collections.singleton(TRANSPORT_WIFI)); + + assertEquals(config, configEqual); + assertNotEquals(config, configNotEqual); + } + + private VcnConfig buildConfigRestrictTransportTest(boolean isTestMode) throws Exception { + VcnConfig.Builder builder = + new VcnConfig.Builder(mContext) + .setRestrictedUnderlyingNetworkTransports(Set.of(TRANSPORT_TEST)); + if (isTestMode) { + builder.setIsTestModeProfile(); + } + + for (VcnGatewayConnectionConfig gatewayConnectionConfig : GATEWAY_CONNECTION_CONFIGS) { + builder.addGatewayConnectionConfig(gatewayConnectionConfig); + } + + return builder.build(); + } + + @Test + public void testRestrictTransportTestInTestModeProfile() throws Exception { + final VcnConfig config = buildConfigRestrictTransportTest(true /* isTestMode */); + assertEquals(Set.of(TRANSPORT_TEST), config.getRestrictedUnderlyingNetworkTransports()); + } + + @Test + public void testRestrictTransportTestInNonTestModeProfile() throws Exception { + try { + buildConfigRestrictTransportTest(false /* isTestMode */); + fail("Expected exception because the config is not a test mode profile"); + } catch (Exception expected) { + + } + } + + @Test public void testParceling() { final VcnConfig config = buildTestConfig(mContext); diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java index 2aef9ae7ca32..a1a39ff173b4 100644 --- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java +++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java @@ -19,9 +19,11 @@ package android.net.vcn; import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE; import static android.net.vcn.VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES; import static android.net.vcn.VcnGatewayConnectionConfig.UNDERLYING_NETWORK_TEMPLATES_KEY; +import static android.net.vcn.VcnGatewayConnectionConfig.VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertTrue; @@ -42,7 +44,9 @@ import org.junit.runner.RunWith; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.Set; import java.util.concurrent.TimeUnit; @RunWith(AndroidJUnit4.class) @@ -78,6 +82,10 @@ public class VcnGatewayConnectionConfigTest { TimeUnit.MINUTES.toMillis(30) }; public static final int MAX_MTU = 1360; + public static final int MIN_UDP_PORT_4500_NAT_TIMEOUT = 120; + + private static final Set<Integer> GATEWAY_OPTIONS = + Collections.singleton(VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY); public static final IkeTunnelConnectionParams TUNNEL_CONNECTION_PARAMS = TunnelConnectionParamsUtilsTest.buildTestParams(); @@ -93,14 +101,22 @@ public class VcnGatewayConnectionConfigTest { EXPOSED_CAPS); } - // Public for use in VcnGatewayConnectionTest - public static VcnGatewayConnectionConfig buildTestConfig() { + // Public for use in UnderlyingNetworkControllerTest + public static VcnGatewayConnectionConfig buildTestConfig( + List<VcnUnderlyingNetworkTemplate> nwTemplates) { final VcnGatewayConnectionConfig.Builder builder = - newBuilder().setVcnUnderlyingNetworkPriorities(UNDERLYING_NETWORK_TEMPLATES); + newBuilder() + .setVcnUnderlyingNetworkPriorities(nwTemplates) + .setMinUdpPort4500NatTimeoutSeconds(MIN_UDP_PORT_4500_NAT_TIMEOUT); return buildTestConfigWithExposedCaps(builder, EXPOSED_CAPS); } + // Public for use in VcnGatewayConnectionTest + public static VcnGatewayConnectionConfig buildTestConfig() { + return buildTestConfig(UNDERLYING_NETWORK_TEMPLATES); + } + private static VcnGatewayConnectionConfig.Builder newBuilder() { // Append a unique identifier to the name prefix to guarantee that all created // VcnGatewayConnectionConfigs have a unique name (required by VcnConfig). @@ -109,10 +125,16 @@ public class VcnGatewayConnectionConfigTest { TUNNEL_CONNECTION_PARAMS); } - private static VcnGatewayConnectionConfig buildTestConfigWithExposedCaps( - VcnGatewayConnectionConfig.Builder builder, int... exposedCaps) { + private static VcnGatewayConnectionConfig buildTestConfigWithExposedCapsAndOptions( + VcnGatewayConnectionConfig.Builder builder, + Set<Integer> gatewayOptions, + int... exposedCaps) { builder.setRetryIntervalsMillis(RETRY_INTERVALS_MS).setMaxMtu(MAX_MTU); + for (int option : gatewayOptions) { + builder.addGatewayOption(option); + } + for (int caps : exposedCaps) { builder.addExposedCapability(caps); } @@ -120,11 +142,28 @@ public class VcnGatewayConnectionConfigTest { return builder.build(); } + private static VcnGatewayConnectionConfig buildTestConfigWithExposedCaps( + VcnGatewayConnectionConfig.Builder builder, int... exposedCaps) { + return buildTestConfigWithExposedCapsAndOptions( + builder, Collections.emptySet(), exposedCaps); + } + // Public for use in VcnGatewayConnectionTest public static VcnGatewayConnectionConfig buildTestConfigWithExposedCaps(int... exposedCaps) { return buildTestConfigWithExposedCaps(newBuilder(), exposedCaps); } + private static VcnGatewayConnectionConfig buildTestConfigWithGatewayOptions( + VcnGatewayConnectionConfig.Builder builder, Set<Integer> gatewayOptions) { + return buildTestConfigWithExposedCapsAndOptions(builder, gatewayOptions, EXPOSED_CAPS); + } + + // Public for use in VcnGatewayConnectionTest + public static VcnGatewayConnectionConfig buildTestConfigWithGatewayOptions( + Set<Integer> gatewayOptions) { + return buildTestConfigWithExposedCapsAndOptions(newBuilder(), gatewayOptions, EXPOSED_CAPS); + } + @Test public void testBuilderRequiresNonNullGatewayConnectionName() { try { @@ -211,6 +250,15 @@ public class VcnGatewayConnectionConfigTest { } @Test + public void testBuilderRequiresValidOption() { + try { + newBuilder().addGatewayOption(-1); + fail("Expected exception due to the invalid VCN gateway option"); + } catch (IllegalArgumentException e) { + } + } + + @Test public void testBuilderAndGetters() { final VcnGatewayConnectionConfig config = buildTestConfig(); @@ -225,6 +273,20 @@ public class VcnGatewayConnectionConfigTest { assertArrayEquals(RETRY_INTERVALS_MS, config.getRetryIntervalsMillis()); assertEquals(MAX_MTU, config.getMaxMtu()); + + assertFalse( + config.hasGatewayOption( + VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY)); + } + + @Test + public void testBuilderAndGettersWithOptions() { + final VcnGatewayConnectionConfig config = + buildTestConfigWithGatewayOptions(GATEWAY_OPTIONS); + + for (int option : GATEWAY_OPTIONS) { + assertTrue(config.hasGatewayOption(option)); + } } @Test @@ -235,6 +297,14 @@ public class VcnGatewayConnectionConfigTest { } @Test + public void testPersistableBundleWithOptions() { + final VcnGatewayConnectionConfig config = + buildTestConfigWithGatewayOptions(GATEWAY_OPTIONS); + + assertEquals(config, new VcnGatewayConnectionConfig(config.toPersistableBundle())); + } + + @Test public void testParsePersistableBundleWithoutVcnUnderlyingNetworkTemplates() { PersistableBundle configBundle = buildTestConfig().toPersistableBundle(); configBundle.putPersistableBundle(UNDERLYING_NETWORK_TEMPLATES_KEY, null); @@ -318,4 +388,27 @@ public class VcnGatewayConnectionConfigTest { assertNotEquals(UNDERLYING_NETWORK_TEMPLATES, networkTemplatesNotEqual); assertNotEquals(config, configNotEqual); } + + private static VcnGatewayConnectionConfig buildConfigWithGatewayOptionsForEqualityTest( + Set<Integer> gatewayOptions) { + return buildTestConfigWithGatewayOptions( + new VcnGatewayConnectionConfig.Builder( + "buildConfigWithGatewayOptionsForEqualityTest", TUNNEL_CONNECTION_PARAMS), + gatewayOptions); + } + + @Test + public void testVcnGatewayOptionsEquality() throws Exception { + final VcnGatewayConnectionConfig config = + buildConfigWithGatewayOptionsForEqualityTest(GATEWAY_OPTIONS); + + final VcnGatewayConnectionConfig configEqual = + buildConfigWithGatewayOptionsForEqualityTest(GATEWAY_OPTIONS); + + final VcnGatewayConnectionConfig configNotEqual = + buildConfigWithGatewayOptionsForEqualityTest(Collections.emptySet()); + + assertEquals(config, configEqual); + assertNotEquals(config, configNotEqual); + } } diff --git a/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java b/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java index 19df3c75266c..81814b67f5ee 100644 --- a/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java +++ b/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java @@ -19,6 +19,7 @@ package android.net.vcn; import static android.net.NetworkCapabilities.REDACT_FOR_ACCESS_FINE_LOCATION; import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS; import static android.net.NetworkCapabilities.REDACT_NONE; +import static android.net.vcn.VcnGatewayConnectionConfig.MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import static org.junit.Assert.assertEquals; @@ -37,11 +38,14 @@ import java.util.Arrays; public class VcnTransportInfoTest { private static final int SUB_ID = 1; private static final int NETWORK_ID = 5; + private static final int MIN_UDP_PORT_4500_NAT_TIMEOUT = 120; private static final WifiInfo WIFI_INFO = new WifiInfo.Builder().setNetworkId(NETWORK_ID).build(); - private static final VcnTransportInfo CELL_UNDERLYING_INFO = new VcnTransportInfo(SUB_ID); - private static final VcnTransportInfo WIFI_UNDERLYING_INFO = new VcnTransportInfo(WIFI_INFO); + private static final VcnTransportInfo CELL_UNDERLYING_INFO = + new VcnTransportInfo(SUB_ID, MIN_UDP_PORT_4500_NAT_TIMEOUT); + private static final VcnTransportInfo WIFI_UNDERLYING_INFO = + new VcnTransportInfo(WIFI_INFO, MIN_UDP_PORT_4500_NAT_TIMEOUT); @Test public void testGetWifiInfo() { @@ -58,6 +62,16 @@ public class VcnTransportInfoTest { } @Test + public void testGetMinUdpPort4500NatTimeoutSeconds() { + assertEquals( + MIN_UDP_PORT_4500_NAT_TIMEOUT, + CELL_UNDERLYING_INFO.getMinUdpPort4500NatTimeoutSeconds()); + assertEquals( + MIN_UDP_PORT_4500_NAT_TIMEOUT, + WIFI_UNDERLYING_INFO.getMinUdpPort4500NatTimeoutSeconds()); + } + + @Test public void testMakeCopyRedactForNetworkSettings() { for (VcnTransportInfo info : Arrays.asList(CELL_UNDERLYING_INFO, WIFI_UNDERLYING_INFO)) { assertEquals( @@ -67,6 +81,10 @@ public class VcnTransportInfoTest { assertNull( ((VcnTransportInfo) info.makeCopy(REDACT_FOR_NETWORK_SETTINGS)) .getWifiInfo()); + assertEquals( + MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET, + ((VcnTransportInfo) info.makeCopy(REDACT_FOR_NETWORK_SETTINGS)) + .getMinUdpPort4500NatTimeoutSeconds()); } } @@ -77,9 +95,17 @@ public class VcnTransportInfoTest { ((VcnTransportInfo) CELL_UNDERLYING_INFO.makeCopy(REDACT_FOR_ACCESS_FINE_LOCATION)) .getSubId()); assertEquals( + MIN_UDP_PORT_4500_NAT_TIMEOUT, + ((VcnTransportInfo) CELL_UNDERLYING_INFO.makeCopy(REDACT_FOR_ACCESS_FINE_LOCATION)) + .getMinUdpPort4500NatTimeoutSeconds()); + assertEquals( WifiConfiguration.INVALID_NETWORK_ID, ((VcnTransportInfo) WIFI_UNDERLYING_INFO.makeCopy(REDACT_FOR_ACCESS_FINE_LOCATION)) .getWifiInfo().getNetworkId()); + assertEquals( + MIN_UDP_PORT_4500_NAT_TIMEOUT, + ((VcnTransportInfo) WIFI_UNDERLYING_INFO.makeCopy(REDACT_FOR_ACCESS_FINE_LOCATION)) + .getMinUdpPort4500NatTimeoutSeconds()); } @Test @@ -110,8 +136,12 @@ public class VcnTransportInfoTest { public void testParcelNotRedactedForSysUi() { VcnTransportInfo cellRedacted = parcelForSysUi(CELL_UNDERLYING_INFO); assertEquals(SUB_ID, cellRedacted.getSubId()); + assertEquals( + MIN_UDP_PORT_4500_NAT_TIMEOUT, cellRedacted.getMinUdpPort4500NatTimeoutSeconds()); VcnTransportInfo wifiRedacted = parcelForSysUi(WIFI_UNDERLYING_INFO); assertEquals(NETWORK_ID, wifiRedacted.getWifiInfo().getNetworkId()); + assertEquals( + MIN_UDP_PORT_4500_NAT_TIMEOUT, wifiRedacted.getMinUdpPort4500NatTimeoutSeconds()); } private VcnTransportInfo parcelForSysUi(VcnTransportInfo vcnTransportInfo) { diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java index e4add8098105..9f7d2390938f 100644 --- a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java +++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java @@ -16,9 +16,9 @@ package android.net.vcn.persistablebundleutils; +import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_AUTOMATIC_KEEPALIVE_ON_OFF; import static android.net.vcn.persistablebundleutils.IkeSessionParamsUtils.IKE_OPTION_AUTOMATIC_ADDRESS_FAMILY_SELECTION; import static android.net.vcn.persistablebundleutils.IkeSessionParamsUtils.IKE_OPTION_AUTOMATIC_NATT_KEEPALIVES; -import static android.net.vcn.persistablebundleutils.IkeSessionParamsUtils.isIkeOptionValid; import static android.system.OsConstants.AF_INET; import static android.system.OsConstants.AF_INET6; import static android.telephony.TelephonyManager.APPTYPE_USIM; @@ -160,13 +160,11 @@ public class IkeSessionParamsUtilsTest { .addIkeOption(IkeSessionParams.IKE_OPTION_MOBIKE) .addIkeOption(IkeSessionParams.IKE_OPTION_FORCE_PORT_4500) .addIkeOption(IkeSessionParams.IKE_OPTION_INITIAL_CONTACT) - .addIkeOption(IkeSessionParams.IKE_OPTION_REKEY_MOBILITY); - if (isIkeOptionValid(IKE_OPTION_AUTOMATIC_ADDRESS_FAMILY_SELECTION)) { - builder.addIkeOption(IKE_OPTION_AUTOMATIC_ADDRESS_FAMILY_SELECTION); - } - if (isIkeOptionValid(IKE_OPTION_AUTOMATIC_NATT_KEEPALIVES)) { - builder.addIkeOption(IKE_OPTION_AUTOMATIC_NATT_KEEPALIVES); - } + .addIkeOption(IkeSessionParams.IKE_OPTION_REKEY_MOBILITY) + .addIkeOption(IKE_OPTION_AUTOMATIC_ADDRESS_FAMILY_SELECTION) + .addIkeOption(IKE_OPTION_AUTOMATIC_NATT_KEEPALIVES) + .addIkeOption(IKE_OPTION_AUTOMATIC_KEEPALIVE_ON_OFF); + verifyPersistableBundleEncodeDecodeIsLossless(builder.build()); } diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java index f924b2e9b932..4123f8070e36 100644 --- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java +++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java @@ -17,10 +17,14 @@ package com.android.server; import static android.net.ConnectivityManager.NetworkCallback; +import static android.net.NetworkCapabilities.NET_CAPABILITY_ENTERPRISE; +import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS; +import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; +import static android.net.vcn.VcnManager.VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY; import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE; import static android.net.vcn.VcnManager.VCN_STATUS_CODE_SAFE_MODE; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; @@ -65,7 +69,6 @@ import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkRequest; -import android.net.TelephonyNetworkSpecifier; import android.net.Uri; import android.net.vcn.IVcnStatusCallback; import android.net.vcn.IVcnUnderlyingNetworkPolicyListener; @@ -95,6 +98,7 @@ import com.android.server.vcn.Vcn; import com.android.server.vcn.VcnContext; import com.android.server.vcn.VcnNetworkProvider; import com.android.server.vcn.util.PersistableBundleUtils; +import com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; import org.junit.Before; import org.junit.Test; @@ -125,6 +129,15 @@ public class VcnManagementServiceTest { private static final VcnConfig TEST_VCN_CONFIG; private static final VcnConfig TEST_VCN_CONFIG_PKG_2; private static final int TEST_UID = Process.FIRST_APPLICATION_UID; + private static final String TEST_IFACE_NAME = "TEST_IFACE"; + private static final String TEST_IFACE_NAME_2 = "TEST_IFACE2"; + private static final LinkProperties TEST_LP_1 = new LinkProperties(); + private static final LinkProperties TEST_LP_2 = new LinkProperties(); + + static { + TEST_LP_1.setInterfaceName(TEST_IFACE_NAME); + TEST_LP_2.setInterfaceName(TEST_IFACE_NAME_2); + } static { final Context mockConfigContext = mock(Context.class); @@ -252,6 +265,10 @@ public class VcnManagementServiceTest { .when(mMockContext) .enforceCallingOrSelfPermission( eq(android.Manifest.permission.NETWORK_FACTORY), any()); + + doReturn(Collections.singleton(TRANSPORT_WIFI)) + .when(mMockDeps) + .getRestrictedTransports(any(), any(), any()); } @@ -637,8 +654,7 @@ public class VcnManagementServiceTest { final BroadcastReceiver receiver = getPackageChangeReceiver(); verify(mMockContext).registerReceiver(any(), argThat(filter -> { - return filter.hasAction(Intent.ACTION_PACKAGE_REMOVED) - && filter.hasAction(Intent.ACTION_PACKAGE_REMOVED); + return filter.hasAction(Intent.ACTION_PACKAGE_REMOVED); }), any(), any()); receiver.onReceive(mMockContext, new Intent(Intent.ACTION_PACKAGE_REMOVED)); @@ -1028,70 +1044,232 @@ public class VcnManagementServiceTest { setupSubscriptionAndStartVcn(subId, subGrp, isVcnActive); return mVcnMgmtSvc.getUnderlyingNetworkPolicy( - getNetworkCapabilitiesBuilderForTransport(subId, transport).build(), - new LinkProperties()); + getNetworkCapabilitiesBuilderForTransport(subId, transport).build(), TEST_LP_1); + } + + private void checkGetRestrictedTransportsFromCarrierConfig( + ParcelUuid subGrp, + TelephonySubscriptionSnapshot lastSnapshot, + Set<Integer> expectedTransports) { + Set<Integer> result = + new VcnManagementService.Dependencies() + .getRestrictedTransportsFromCarrierConfig(subGrp, lastSnapshot); + assertEquals(expectedTransports, result); + } + + @Test + public void testGetRestrictedTransportsFromCarrierConfig() { + final Set<Integer> restrictedTransports = new ArraySet<>(); + restrictedTransports.add(TRANSPORT_CELLULAR); + restrictedTransports.add(TRANSPORT_WIFI); + + PersistableBundle carrierConfigBundle = new PersistableBundle(); + carrierConfigBundle.putIntArray( + VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY, + restrictedTransports.stream().mapToInt(i -> i).toArray()); + final PersistableBundleWrapper carrierConfig = + new PersistableBundleWrapper(carrierConfigBundle); + + final TelephonySubscriptionSnapshot lastSnapshot = + mock(TelephonySubscriptionSnapshot.class); + doReturn(carrierConfig).when(lastSnapshot).getCarrierConfigForSubGrp(eq(TEST_UUID_2)); + + checkGetRestrictedTransportsFromCarrierConfig( + TEST_UUID_2, lastSnapshot, restrictedTransports); + } + + @Test + public void testGetRestrictedTransportsFromCarrierConfig_noRestrictPolicyConfigured() { + final Set<Integer> restrictedTransports = Collections.singleton(TRANSPORT_WIFI); + + final PersistableBundleWrapper carrierConfig = + new PersistableBundleWrapper(new PersistableBundle()); + final TelephonySubscriptionSnapshot lastSnapshot = + mock(TelephonySubscriptionSnapshot.class); + doReturn(carrierConfig).when(lastSnapshot).getCarrierConfigForSubGrp(eq(TEST_UUID_2)); + + checkGetRestrictedTransportsFromCarrierConfig( + TEST_UUID_2, lastSnapshot, restrictedTransports); } @Test - public void testGetUnderlyingNetworkPolicyCellular() throws Exception { + public void testGetRestrictedTransportsFromCarrierConfig_noCarrierConfig() { + final Set<Integer> restrictedTransports = Collections.singleton(TRANSPORT_WIFI); + + final TelephonySubscriptionSnapshot lastSnapshot = + mock(TelephonySubscriptionSnapshot.class); + + checkGetRestrictedTransportsFromCarrierConfig( + TEST_UUID_2, lastSnapshot, restrictedTransports); + } + + @Test + public void testGetRestrictedTransportsFromCarrierConfigAndVcnConfig() { + // Configure restricted transport in CarrierConfig + final Set<Integer> restrictedTransportInCarrierConfig = + Collections.singleton(TRANSPORT_WIFI); + + PersistableBundle carrierConfigBundle = new PersistableBundle(); + carrierConfigBundle.putIntArray( + VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY, + restrictedTransportInCarrierConfig.stream().mapToInt(i -> i).toArray()); + final PersistableBundleWrapper carrierConfig = + new PersistableBundleWrapper(carrierConfigBundle); + + final TelephonySubscriptionSnapshot lastSnapshot = + mock(TelephonySubscriptionSnapshot.class); + doReturn(carrierConfig).when(lastSnapshot).getCarrierConfigForSubGrp(eq(TEST_UUID_2)); + + // Configure restricted transport in VcnConfig + final Context mockContext = mock(Context.class); + doReturn(TEST_PACKAGE_NAME).when(mockContext).getOpPackageName(); + final VcnConfig vcnConfig = + VcnConfigTest.buildTestConfig( + mockContext, Collections.singleton(TRANSPORT_CELLULAR)); + + // Verifications + final Set<Integer> expectedTransports = new ArraySet<>(); + expectedTransports.add(TRANSPORT_CELLULAR); + expectedTransports.add(TRANSPORT_WIFI); + + Set<Integer> result = + new VcnManagementService.Dependencies() + .getRestrictedTransports(TEST_UUID_2, lastSnapshot, vcnConfig); + assertEquals(expectedTransports, result); + } + + private void checkGetUnderlyingNetworkPolicy( + int transportType, + boolean isTransportRestricted, + boolean isActive, + boolean expectVcnManaged, + boolean expectRestricted) + throws Exception { + + final Set<Integer> restrictedTransports = new ArraySet(); + if (isTransportRestricted) { + restrictedTransports.add(transportType); + } + doReturn(restrictedTransports).when(mMockDeps).getRestrictedTransports(any(), any(), any()); + final VcnUnderlyingNetworkPolicy policy = startVcnAndGetPolicyForTransport( - TEST_SUBSCRIPTION_ID, TEST_UUID_2, true /* isActive */, TRANSPORT_CELLULAR); + TEST_SUBSCRIPTION_ID, TEST_UUID_2, isActive, transportType); assertFalse(policy.isTeardownRequested()); verifyMergedNetworkCapabilities( policy.getMergedNetworkCapabilities(), + transportType, + expectVcnManaged, + expectRestricted); + } + + @Test + public void testGetUnderlyingNetworkPolicy_unrestrictCell() throws Exception { + checkGetUnderlyingNetworkPolicy( TRANSPORT_CELLULAR, - true /* isVcnManaged */, - false /* isRestricted */); + false /* isTransportRestricted */, + true /* isActive */, + true /* expectVcnManaged */, + false /* expectRestricted */); } @Test - public void testGetUnderlyingNetworkPolicyCellular_safeMode() throws Exception { - final VcnUnderlyingNetworkPolicy policy = - startVcnAndGetPolicyForTransport( - TEST_SUBSCRIPTION_ID, - TEST_UUID_2, - false /* isActive */, - TRANSPORT_CELLULAR); + public void testGetUnderlyingNetworkPolicy_unrestrictCellSafeMode() throws Exception { + checkGetUnderlyingNetworkPolicy( + TRANSPORT_CELLULAR, + false /* isTransportRestricted */, + false /* isActive */, + false /* expectVcnManaged */, + false /* expectRestricted */); + } - assertFalse(policy.isTeardownRequested()); - verifyMergedNetworkCapabilities( - policy.getMergedNetworkCapabilities(), - NetworkCapabilities.TRANSPORT_CELLULAR, - false /* isVcnManaged */, - false /* isRestricted */); + @Test + public void testGetUnderlyingNetworkPolicy_restrictCell() throws Exception { + checkGetUnderlyingNetworkPolicy( + TRANSPORT_CELLULAR, + true /* isTransportRestricted */, + true /* isActive */, + true /* expectVcnManaged */, + true /* expectRestricted */); } @Test - public void testGetUnderlyingNetworkPolicyWifi() throws Exception { - final VcnUnderlyingNetworkPolicy policy = - startVcnAndGetPolicyForTransport( - TEST_SUBSCRIPTION_ID, TEST_UUID_2, true /* isActive */, TRANSPORT_WIFI); + public void testGetUnderlyingNetworkPolicy_restrictCellSafeMode() throws Exception { + checkGetUnderlyingNetworkPolicy( + TRANSPORT_CELLULAR, + true /* isTransportRestricted */, + false /* isActive */, + false /* expectVcnManaged */, + false /* expectRestricted */); + } - assertFalse(policy.isTeardownRequested()); - verifyMergedNetworkCapabilities( - policy.getMergedNetworkCapabilities(), - NetworkCapabilities.TRANSPORT_WIFI, - true /* isVcnManaged */, - true /* isRestricted */); + @Test + public void testGetUnderlyingNetworkPolicy_unrestrictWifi() throws Exception { + checkGetUnderlyingNetworkPolicy( + TRANSPORT_WIFI, + false /* isTransportRestricted */, + true /* isActive */, + true /* expectVcnManaged */, + false /* expectRestricted */); + } + + @Test + public void testGetUnderlyingNetworkPolicy_unrestrictWifiSafeMode() throws Exception { + checkGetUnderlyingNetworkPolicy( + TRANSPORT_WIFI, + false /* isTransportRestricted */, + false /* isActive */, + false /* expectVcnManaged */, + false /* expectRestricted */); + } + + @Test + public void testGetUnderlyingNetworkPolicy_restrictWifi() throws Exception { + checkGetUnderlyingNetworkPolicy( + TRANSPORT_WIFI, + true /* isTransportRestricted */, + true /* isActive */, + true /* expectVcnManaged */, + true /* expectRestricted */); + } + + @Test + public void testGetUnderlyingNetworkPolicy_restrictWifiSafeMode() throws Exception { + checkGetUnderlyingNetworkPolicy( + TRANSPORT_WIFI, + true /* isTransportRestricted */, + false /* isActive */, + false /* expectVcnManaged */, + true /* expectRestricted */); } @Test - public void testGetUnderlyingNetworkPolicyVcnWifi_safeMode() throws Exception { + public void testGetUnderlyingNetworkPolicyCell_restrictWifi() throws Exception { + doReturn(Collections.singleton(TRANSPORT_WIFI)) + .when(mMockDeps) + .getRestrictedTransports(any(), any(), any()); + + setupSubscriptionAndStartVcn(TEST_SUBSCRIPTION_ID, TEST_UUID_2, true /* isVcnActive */); + + // Get the policy for a cellular network and expect it won't be affected by the wifi + // restriction policy final VcnUnderlyingNetworkPolicy policy = - startVcnAndGetPolicyForTransport( - TEST_SUBSCRIPTION_ID, TEST_UUID_2, false /* isActive */, TRANSPORT_WIFI); + mVcnMgmtSvc.getUnderlyingNetworkPolicy( + getNetworkCapabilitiesBuilderForTransport( + TEST_SUBSCRIPTION_ID, TRANSPORT_CELLULAR) + .build(), + new LinkProperties()); assertFalse(policy.isTeardownRequested()); verifyMergedNetworkCapabilities( policy.getMergedNetworkCapabilities(), - NetworkCapabilities.TRANSPORT_WIFI, - false /* isVcnManaged */, - true /* isRestricted */); + TRANSPORT_CELLULAR, + true /* expectVcnManaged */, + false /* expectRestricted */); } - private void setupTrackedCarrierWifiNetwork(NetworkCapabilities caps) { + private void setupTrackedNetwork(NetworkCapabilities caps, LinkProperties lp) { mVcnMgmtSvc.systemReady(); final ArgumentCaptor<NetworkCallback> captor = @@ -1100,7 +1278,10 @@ public class VcnManagementServiceTest { .registerNetworkCallback( eq(new NetworkRequest.Builder().clearCapabilities().build()), captor.capture()); - captor.getValue().onCapabilitiesChanged(mock(Network.class, CALLS_REAL_METHODS), caps); + + Network mockNetwork = mock(Network.class, CALLS_REAL_METHODS); + captor.getValue().onCapabilitiesChanged(mockNetwork, caps); + captor.getValue().onLinkPropertiesChanged(mockNetwork, lp); } @Test @@ -1110,7 +1291,7 @@ public class VcnManagementServiceTest { getNetworkCapabilitiesBuilderForTransport(TEST_SUBSCRIPTION_ID, TRANSPORT_WIFI) .removeCapability(NET_CAPABILITY_NOT_RESTRICTED) .build(); - setupTrackedCarrierWifiNetwork(existingNetworkCaps); + setupTrackedNetwork(existingNetworkCaps, TEST_LP_1); // Trigger test without VCN instance alive; expect restart due to change of NOT_RESTRICTED // immutable capability @@ -1119,7 +1300,7 @@ public class VcnManagementServiceTest { getNetworkCapabilitiesBuilderForTransport( TEST_SUBSCRIPTION_ID, TRANSPORT_WIFI) .build(), - new LinkProperties()); + TEST_LP_1); assertTrue(policy.isTeardownRequested()); } @@ -1129,7 +1310,7 @@ public class VcnManagementServiceTest { final NetworkCapabilities existingNetworkCaps = getNetworkCapabilitiesBuilderForTransport(TEST_SUBSCRIPTION_ID, TRANSPORT_WIFI) .build(); - setupTrackedCarrierWifiNetwork(existingNetworkCaps); + setupTrackedNetwork(existingNetworkCaps, TEST_LP_1); final VcnUnderlyingNetworkPolicy policy = startVcnAndGetPolicyForTransport( @@ -1139,6 +1320,27 @@ public class VcnManagementServiceTest { } @Test + public void testGetUnderlyingNetworkPolicyForRestrictedImsWhenUnrestrictingCell() + throws Exception { + final NetworkCapabilities existingNetworkCaps = + getNetworkCapabilitiesBuilderForTransport(TEST_SUBSCRIPTION_ID, TRANSPORT_CELLULAR) + .addCapability(NET_CAPABILITY_NOT_RESTRICTED) + .removeCapability(NET_CAPABILITY_IMS) + .build(); + setupTrackedNetwork(existingNetworkCaps, TEST_LP_1); + + final VcnUnderlyingNetworkPolicy policy = + mVcnMgmtSvc.getUnderlyingNetworkPolicy( + getNetworkCapabilitiesBuilderForTransport( + TEST_SUBSCRIPTION_ID, TRANSPORT_CELLULAR) + .addCapability(NET_CAPABILITY_IMS) + .removeCapability(NET_CAPABILITY_NOT_RESTRICTED) + .build(), + new LinkProperties()); + assertFalse(policy.isTeardownRequested()); + } + + @Test public void testGetUnderlyingNetworkPolicyNonVcnNetwork() throws Exception { setupSubscriptionAndStartVcn(TEST_SUBSCRIPTION_ID, TEST_UUID_1, true /* isActive */); @@ -1146,7 +1348,7 @@ public class VcnManagementServiceTest { new NetworkCapabilities.Builder() .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) - .setNetworkSpecifier(new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID_2)) + .setSubscriptionIds(Collections.singleton(TEST_SUBSCRIPTION_ID_2)) .build(); VcnUnderlyingNetworkPolicy policy = @@ -1156,6 +1358,38 @@ public class VcnManagementServiceTest { assertEquals(nc, policy.getMergedNetworkCapabilities()); } + /** + * Checks that networks with similar capabilities do not clobber each other. + * + * <p>In previous iterations, the VcnMgmtSvc used capability-matching to check if a network + * undergoing policy checks were the same as an existing networks. However, this meant that if + * there were newly added capabilities that the VCN did not check, two networks differing only + * by that capability would restart each other constantly. + */ + @Test + public void testGetUnderlyingNetworkPolicySimilarNetworks() throws Exception { + NetworkCapabilities nc1 = + new NetworkCapabilities.Builder() + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) + .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) + .addCapability(NET_CAPABILITY_INTERNET) + .setSubscriptionIds(Collections.singleton(TEST_SUBSCRIPTION_ID_2)) + .build(); + + NetworkCapabilities nc2 = + new NetworkCapabilities.Builder(nc1) + .addCapability(NET_CAPABILITY_ENTERPRISE) + .removeCapability(NET_CAPABILITY_NOT_RESTRICTED) + .build(); + + setupTrackedNetwork(nc1, TEST_LP_1); + + VcnUnderlyingNetworkPolicy policy = mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc2, TEST_LP_2); + + assertFalse(policy.isTeardownRequested()); + assertEquals(nc2, policy.getMergedNetworkCapabilities()); + } + @Test(expected = SecurityException.class) public void testGetUnderlyingNetworkPolicyInvalidPermission() { doReturn(PackageManager.PERMISSION_DENIED) @@ -1192,6 +1426,23 @@ public class VcnManagementServiceTest { } @Test + public void testVcnConfigChangeUpdatesPolicyListener() throws Exception { + setupActiveSubscription(TEST_UUID_2); + + mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME); + mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); + + final Context mockContext = mock(Context.class); + doReturn(TEST_PACKAGE_NAME).when(mockContext).getOpPackageName(); + final VcnConfig vcnConfig = + VcnConfigTest.buildTestConfig( + mockContext, Collections.singleton(TRANSPORT_CELLULAR)); + mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, vcnConfig, TEST_PACKAGE_NAME); + + verify(mMockPolicyListener).onPolicyChanged(); + } + + @Test public void testRemoveVcnUpdatesPolicyListener() throws Exception { setupActiveSubscription(TEST_UUID_2); @@ -1218,6 +1469,30 @@ public class VcnManagementServiceTest { verify(mMockPolicyListener).onPolicyChanged(); } + @Test + public void testVcnCarrierConfigChangeUpdatesPolicyListener() throws Exception { + setupActiveSubscription(TEST_UUID_2); + + mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME); + mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); + + final TelephonySubscriptionSnapshot snapshot = + buildSubscriptionSnapshot( + TEST_SUBSCRIPTION_ID, + TEST_UUID_2, + Collections.singleton(TEST_UUID_2), + Collections.emptyMap(), + true /* hasCarrierPrivileges */); + + final PersistableBundleWrapper mockCarrierConfig = mock(PersistableBundleWrapper.class); + doReturn(mockCarrierConfig).when(snapshot).getCarrierConfigForSubGrp(eq(TEST_UUID_2)); + + final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback(); + cb.onNewSnapshot(snapshot); + + verify(mMockPolicyListener).onPolicyChanged(); + } + private void triggerVcnSafeMode( @NonNull ParcelUuid subGroup, @NonNull TelephonySubscriptionSnapshot snapshot, diff --git a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java index 09080be9ee41..965b073ff0db 100644 --- a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java +++ b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java @@ -16,6 +16,9 @@ package com.android.server.vcn; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; +import static android.net.NetworkCapabilities.TRANSPORT_WIFI; +import static android.net.vcn.VcnManager.VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY; import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED; import static android.telephony.CarrierConfigManager.EXTRA_SLOT_INDEX; import static android.telephony.CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX; @@ -39,6 +42,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -494,6 +498,37 @@ public class TelephonySubscriptionTrackerTest { } @Test + public void testCarrierConfigUpdatedAfterValidTriggersCallbacks() throws Exception { + mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true)); + mTestLooper.dispatchAll(); + verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(TEST_PRIVILEGED_PACKAGES))); + reset(mCallback); + + final PersistableBundle updatedConfig = new PersistableBundle(); + updatedConfig.putIntArray( + VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY, + new int[] {TRANSPORT_WIFI, TRANSPORT_CELLULAR}); + doReturn(updatedConfig) + .when(mCarrierConfigManager) + .getConfigForSubId(eq(TEST_SUBSCRIPTION_ID_1)); + + Map<Integer, PersistableBundleWrapper> subIdToCarrierConfigMap = new HashMap<>(); + subIdToCarrierConfigMap.put( + TEST_SUBSCRIPTION_ID_1, new PersistableBundleWrapper(updatedConfig)); + mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true)); + mTestLooper.dispatchAll(); + + verify(mCallback) + .onNewSnapshot( + eq( + buildExpectedSnapshot( + 0, + TEST_SUBID_TO_INFO_MAP, + subIdToCarrierConfigMap, + TEST_PRIVILEGED_PACKAGES))); + } + + @Test public void testSlotClearedAfterValidTriggersCallbacks() throws Exception { mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true)); mTestLooper.dispatchAll(); diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java index 15d4f1097108..89271e1218d8 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java @@ -25,6 +25,7 @@ import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED; import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_TEMPORARY_FAILURE; +import static android.net.vcn.VcnGatewayConnectionConfigTest.MIN_UDP_PORT_4500_NAT_TIMEOUT; import static android.net.vcn.VcnManager.VCN_ERROR_CODE_CONFIG_ERROR; import static android.net.vcn.VcnManager.VCN_ERROR_CODE_INTERNAL_ERROR; import static android.net.vcn.VcnManager.VCN_ERROR_CODE_NETWORK_ERROR; @@ -50,23 +51,30 @@ import static org.mockito.Mockito.when; import static java.util.Collections.singletonList; +import android.net.ConnectivityDiagnosticsManager.DataStallReport; import android.net.ConnectivityManager; import android.net.LinkAddress; import android.net.LinkProperties; +import android.net.Network; import android.net.NetworkAgent; import android.net.NetworkCapabilities; import android.net.ipsec.ike.ChildSaProposal; import android.net.ipsec.ike.IkeSessionConnectionInfo; +import android.net.ipsec.ike.TunnelModeChildSessionParams; import android.net.ipsec.ike.exceptions.IkeException; import android.net.ipsec.ike.exceptions.IkeInternalException; import android.net.ipsec.ike.exceptions.IkeProtocolException; import android.net.vcn.VcnGatewayConnectionConfig; import android.net.vcn.VcnGatewayConnectionConfigTest; import android.net.vcn.VcnManager.VcnErrorCode; +import android.net.vcn.VcnTransportInfo; +import android.os.PersistableBundle; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.server.vcn.VcnGatewayConnection.VcnChildSessionCallback; +import com.android.server.vcn.routeselection.UnderlyingNetworkRecord; import com.android.server.vcn.util.MtuUtils; import org.junit.Before; @@ -86,8 +94,11 @@ import java.util.function.Consumer; @RunWith(AndroidJUnit4.class) @SmallTest public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnectionTestBase { + private static final int PARALLEL_SA_COUNT = 4; + private VcnIkeSession mIkeSession; private VcnNetworkAgent mNetworkAgent; + private Network mVcnNetwork; @Before public void setUp() throws Exception { @@ -98,6 +109,9 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection .when(mDeps) .newNetworkAgent(any(), any(), any(), any(), any(), any(), any(), any(), any()); + mVcnNetwork = mock(Network.class); + doReturn(mVcnNetwork).when(mNetworkAgent).getNetwork(); + mGatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_1); mIkeSession = mGatewayConnection.buildIkeSession(TEST_UNDERLYING_NETWORK_RECORD_1.network); @@ -166,19 +180,82 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState()); } + private void verifyDataStallTriggersMigration( + UnderlyingNetworkRecord networkRecord, + Network networkWithDataStall, + boolean expectMobilityUpdate) + throws Exception { + mGatewayConnection.setUnderlyingNetwork(networkRecord); + triggerChildOpened(); + mTestLooper.dispatchAll(); + + final DataStallReport report = + new DataStallReport( + networkWithDataStall, + 1234 /* reportTimestamp */, + 1 /* detectionMethod */, + new LinkProperties(), + new NetworkCapabilities(), + new PersistableBundle()); + + mGatewayConnection.getConnectivityDiagnosticsCallback().onDataStallSuspected(report); + mTestLooper.dispatchAll(); + + assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState()); + + if (expectMobilityUpdate) { + verify(mIkeSession).setNetwork(networkRecord.network); + } else { + verify(mIkeSession, never()).setNetwork(any(Network.class)); + } + } + + @Test + public void testDataStallTriggersMigration() throws Exception { + verifyDataStallTriggersMigration( + TEST_UNDERLYING_NETWORK_RECORD_1, mVcnNetwork, true /* expectMobilityUpdate */); + } + + @Test + public void testDataStallWontTriggerMigrationWhenOnOtherNetwork() throws Exception { + verifyDataStallTriggersMigration( + TEST_UNDERLYING_NETWORK_RECORD_1, + mock(Network.class), + false /* expectMobilityUpdate */); + } + + @Test + public void testDataStallWontTriggerMigrationWhenUnderlyingNetworkLost() throws Exception { + verifyDataStallTriggersMigration( + null /* networkRecord */, mock(Network.class), false /* expectMobilityUpdate */); + } + private void verifyVcnTransformsApplied( VcnGatewayConnection vcnGatewayConnection, boolean expectForwardTransform) throws Exception { + verifyVcnTransformsApplied( + vcnGatewayConnection, + expectForwardTransform, + Collections.singletonList(getChildSessionCallback())); + } + + private void verifyVcnTransformsApplied( + VcnGatewayConnection vcnGatewayConnection, + boolean expectForwardTransform, + List<VcnChildSessionCallback> callbacks) + throws Exception { for (int direction : new int[] {DIRECTION_IN, DIRECTION_OUT}) { - getChildSessionCallback().onIpSecTransformCreated(makeDummyIpSecTransform(), direction); + for (VcnChildSessionCallback cb : callbacks) { + cb.onIpSecTransformCreated(makeDummyIpSecTransform(), direction); + } mTestLooper.dispatchAll(); - verify(mIpSecSvc) + verify(mIpSecSvc, times(callbacks.size())) .applyTunnelModeTransform( eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(direction), anyInt(), any()); } - verify(mIpSecSvc, expectForwardTransform ? times(1) : never()) + verify(mIpSecSvc, expectForwardTransform ? times(callbacks.size()) : never()) .applyTunnelModeTransform( eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(DIRECTION_FWD), anyInt(), any()); @@ -350,6 +427,12 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection assertTrue(nc.hasCapability(cap)); } + assertTrue(nc.getTransportInfo() instanceof VcnTransportInfo); + final VcnTransportInfo vcnTransportInfo = (VcnTransportInfo) nc.getTransportInfo(); + assertEquals( + MIN_UDP_PORT_4500_NAT_TIMEOUT, + vcnTransportInfo.getMinUdpPort4500NatTimeoutSeconds()); + // Now that Vcn Network is up, notify it as validated and verify the SafeMode alarm is // canceled triggerValidation(NetworkAgent.VALIDATION_STATUS_VALID); @@ -358,6 +441,89 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection verifySafeModeStateAndCallbackFired(1 /* invocationCount */, false /* isInSafeMode */); } + private List<VcnChildSessionCallback> openChildAndVerifyParallelSasRequested() + throws Exception { + doReturn(PARALLEL_SA_COUNT) + .when(mDeps) + .getParallelTunnelCount(eq(TEST_SUBSCRIPTION_SNAPSHOT), eq(TEST_SUB_GRP)); + + // Verify scheduled but not canceled when entering ConnectedState + verifySafeModeTimeoutAlarmAndGetCallback(false /* expectCanceled */); + triggerChildOpened(); + mTestLooper.dispatchAll(); + + // Verify new child sessions requested + final ArgumentCaptor<VcnChildSessionCallback> captor = + ArgumentCaptor.forClass(VcnChildSessionCallback.class); + verify(mIkeSession, times(PARALLEL_SA_COUNT - 1)) + .openChildSession(any(TunnelModeChildSessionParams.class), captor.capture()); + + return captor.getAllValues(); + } + + private List<VcnChildSessionCallback> verifyChildOpenedRequestsAndAppliesParallelSas() + throws Exception { + List<VcnChildSessionCallback> callbacks = openChildAndVerifyParallelSasRequested(); + + verifyVcnTransformsApplied(mGatewayConnection, false, callbacks); + + // Mock IKE calling of onOpened() + for (VcnChildSessionCallback cb : callbacks) { + cb.onOpened(mock(VcnChildSessionConfiguration.class)); + } + mTestLooper.dispatchAll(); + + assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState()); + return callbacks; + } + + @Test + public void testChildOpenedWithParallelSas() throws Exception { + verifyChildOpenedRequestsAndAppliesParallelSas(); + } + + @Test + public void testOpportunisticSa_ignoresPreOpenFailures() throws Exception { + List<VcnChildSessionCallback> callbacks = openChildAndVerifyParallelSasRequested(); + + for (VcnChildSessionCallback cb : callbacks) { + cb.onClosed(); + cb.onClosedExceptionally(mock(IkeException.class)); + } + mTestLooper.dispatchAll(); + + assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState()); + assertEquals(mIkeConnectionInfo, mGatewayConnection.getIkeConnectionInfo()); + } + + private void verifyPostOpenFailuresCloseSession(boolean shouldCloseWithException) + throws Exception { + List<VcnChildSessionCallback> callbacks = verifyChildOpenedRequestsAndAppliesParallelSas(); + + for (VcnChildSessionCallback cb : callbacks) { + if (shouldCloseWithException) { + cb.onClosed(); + } else { + cb.onClosedExceptionally(mock(IkeException.class)); + } + } + mTestLooper.dispatchAll(); + + assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState()); + verify(mIkeSession).close(); + } + + @Test + public void testOpportunisticSa_handlesPostOpenFailures_onClosed() throws Exception { + verifyPostOpenFailuresCloseSession(false /* shouldCloseWithException */); + } + + @Test + public void testOpportunisticSa_handlesPostOpenFailures_onClosedExceptionally() + throws Exception { + verifyPostOpenFailuresCloseSession(true /* shouldCloseWithException */); + } + @Test public void testInternalAndDnsAddressesChanged() throws Exception { final List<LinkAddress> startingInternalAddrs = diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java index 6a9a1e22cab1..692c8a8f0898 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java @@ -24,6 +24,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; +import static android.net.vcn.VcnGatewayConnectionConfig.VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY; import static com.android.server.vcn.VcnGatewayConnection.DUMMY_ADDR; import static com.android.server.vcn.VcnGatewayConnection.VcnChildSessionConfiguration; @@ -34,20 +35,25 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.CALLS_REAL_METHODS; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback; import android.net.IpSecManager; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; +import android.net.NetworkRequest; import android.net.TelephonyNetworkSpecifier; +import android.net.vcn.VcnGatewayConnectionConfig; import android.net.vcn.VcnGatewayConnectionConfigTest; import android.net.vcn.VcnTransportInfo; import android.net.wifi.WifiInfo; @@ -64,6 +70,7 @@ import com.android.server.vcn.routeselection.UnderlyingNetworkRecord; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import java.net.InetAddress; import java.util.Arrays; @@ -71,7 +78,9 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.UUID; +import java.util.concurrent.Executor; /** Tests for TelephonySubscriptionTracker */ @RunWith(AndroidJUnit4.class) @@ -134,9 +143,9 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase { capBuilder.setLinkDownstreamBandwidthKbps(TEST_DOWNSTREAM_BANDWIDTH); capBuilder.setAdministratorUids(new int[] {TEST_UID}); final Network underlyingNetwork = mock(Network.class, CALLS_REAL_METHODS); - UnderlyingNetworkRecord record = new UnderlyingNetworkRecord( - underlyingNetwork, - capBuilder.build(), new LinkProperties(), false); + UnderlyingNetworkRecord record = + getTestNetworkRecord( + underlyingNetwork, capBuilder.build(), new LinkProperties(), false); final NetworkCapabilities vcnCaps = VcnGatewayConnection.buildNetworkCapabilities( VcnGatewayConnectionConfigTest.buildTestConfig(), @@ -202,7 +211,7 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase { doReturn(TEST_DNS_ADDRESSES).when(childSessionConfig).getInternalDnsServers(); UnderlyingNetworkRecord record = - new UnderlyingNetworkRecord( + getTestNetworkRecord( mock(Network.class, CALLS_REAL_METHODS), new NetworkCapabilities.Builder().build(), underlyingLp, @@ -287,5 +296,60 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase { verify(vcnNetworkAgent).unregister(); verifyWakeLockReleased(); + + verify(mConnDiagMgr) + .unregisterConnectivityDiagnosticsCallback( + mGatewayConnection.getConnectivityDiagnosticsCallback()); + } + + private VcnGatewayConnection buildConnectionWithDataStallHandling( + boolean datatStallHandlingEnabled) throws Exception { + Set<Integer> options = + datatStallHandlingEnabled + ? Collections.singleton( + VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY) + : Collections.emptySet(); + final VcnGatewayConnectionConfig gatewayConfig = + VcnGatewayConnectionConfigTest.buildTestConfigWithGatewayOptions(options); + final VcnGatewayConnection gatewayConnection = + new VcnGatewayConnection( + mVcnContext, + TEST_SUB_GRP, + TEST_SUBSCRIPTION_SNAPSHOT, + gatewayConfig, + mGatewayStatusCallback, + true /* isMobileDataEnabled */, + mDeps); + return gatewayConnection; + } + + @Test + public void testDataStallHandlingEnabled() throws Exception { + final VcnGatewayConnection gatewayConnection = + buildConnectionWithDataStallHandling(true /* datatStallHandlingEnabled */); + + final ArgumentCaptor<NetworkRequest> networkRequestCaptor = + ArgumentCaptor.forClass(NetworkRequest.class); + verify(mConnDiagMgr) + .registerConnectivityDiagnosticsCallback( + networkRequestCaptor.capture(), + any(Executor.class), + eq(gatewayConnection.getConnectivityDiagnosticsCallback())); + + final NetworkRequest nr = networkRequestCaptor.getValue(); + final NetworkRequest expected = + new NetworkRequest.Builder().addTransportType(TRANSPORT_CELLULAR).build(); + assertEquals(expected, nr); + } + + @Test + public void testDataStallHandlingDisabled() throws Exception { + buildConnectionWithDataStallHandling(false /* datatStallHandlingEnabled */); + + verify(mConnDiagMgr, never()) + .registerConnectivityDiagnosticsCallback( + any(NetworkRequest.class), + any(Executor.class), + any(ConnectivityDiagnosticsCallback.class)); } } diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java index 785bff167ad2..5efbf598f941 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java @@ -16,6 +16,8 @@ package com.android.server.vcn; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; + import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession; import static com.android.server.vcn.VcnGatewayConnection.VcnNetworkAgent; import static com.android.server.vcn.VcnTestUtils.setupIpSecManager; @@ -35,6 +37,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import android.annotation.NonNull; import android.content.Context; +import android.net.ConnectivityDiagnosticsManager; import android.net.ConnectivityManager; import android.net.InetAddresses; import android.net.IpSecConfig; @@ -45,6 +48,7 @@ import android.net.LinkAddress; import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; +import android.net.TelephonyNetworkSpecifier; import android.net.ipsec.ike.ChildSessionCallback; import android.net.ipsec.ike.IkeSessionCallback; import android.net.ipsec.ike.IkeSessionConfiguration; @@ -108,11 +112,28 @@ public class VcnGatewayConnectionTestBase { protected static final long ELAPSED_REAL_TIME = 123456789L; protected static final String TEST_IPSEC_TUNNEL_IFACE = "IPSEC_IFACE"; + protected static UnderlyingNetworkRecord getTestNetworkRecord( + Network network, + NetworkCapabilities networkCapabilities, + LinkProperties linkProperties, + boolean isBlocked) { + return new UnderlyingNetworkRecord( + network, + networkCapabilities, + linkProperties, + isBlocked, + false /* isSelected */, + 0 /* priorityClass */); + } + protected static final String TEST_TCP_BUFFER_SIZES_1 = "1,2,3,4"; protected static final UnderlyingNetworkRecord TEST_UNDERLYING_NETWORK_RECORD_1 = - new UnderlyingNetworkRecord( + getTestNetworkRecord( mock(Network.class, CALLS_REAL_METHODS), - new NetworkCapabilities(), + new NetworkCapabilities.Builder() + .addTransportType(TRANSPORT_CELLULAR) + .setNetworkSpecifier(new TelephonyNetworkSpecifier(TEST_SUB_ID)) + .build(), new LinkProperties(), false /* blocked */); @@ -123,7 +144,7 @@ public class VcnGatewayConnectionTestBase { protected static final String TEST_TCP_BUFFER_SIZES_2 = "2,3,4,5"; protected static final UnderlyingNetworkRecord TEST_UNDERLYING_NETWORK_RECORD_2 = - new UnderlyingNetworkRecord( + getTestNetworkRecord( mock(Network.class, CALLS_REAL_METHODS), new NetworkCapabilities(), new LinkProperties(), @@ -157,6 +178,7 @@ public class VcnGatewayConnectionTestBase { @NonNull protected final IpSecService mIpSecSvc; @NonNull protected final ConnectivityManager mConnMgr; + @NonNull protected final ConnectivityDiagnosticsManager mConnDiagMgr; @NonNull protected final IkeSessionConnectionInfo mIkeConnectionInfo; @NonNull protected final IkeSessionConfiguration mIkeSessionConfiguration; @@ -186,6 +208,13 @@ public class VcnGatewayConnectionTestBase { VcnTestUtils.setupSystemService( mContext, mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class); + mConnDiagMgr = mock(ConnectivityDiagnosticsManager.class); + VcnTestUtils.setupSystemService( + mContext, + mConnDiagMgr, + Context.CONNECTIVITY_DIAGNOSTICS_SERVICE, + ConnectivityDiagnosticsManager.class); + mIkeConnectionInfo = new IkeSessionConnectionInfo(TEST_ADDR, TEST_ADDR_2, mock(Network.class)); mIkeSessionConfiguration = new IkeSessionConfiguration.Builder(mIkeConnectionInfo).build(); @@ -200,6 +229,9 @@ public class VcnGatewayConnectionTestBase { doReturn(mWakeLock) .when(mDeps) .newWakeLock(eq(mContext), eq(PowerManager.PARTIAL_WAKE_LOCK), any()); + doReturn(1) + .when(mDeps) + .getParallelTunnelCount(eq(TEST_SUBSCRIPTION_SNAPSHOT), eq(TEST_SUB_GRP)); setUpWakeupMessage(mTeardownTimeoutAlarm, VcnGatewayConnection.TEARDOWN_TIMEOUT_ALARM); setUpWakeupMessage(mDisconnectRequestAlarm, VcnGatewayConnection.DISCONNECT_REQUEST_ALARM); diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java index b0d68952c39d..226604108522 100644 --- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java +++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java @@ -16,6 +16,7 @@ package com.android.server.vcn.routeselection; +import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN; import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED; import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS; @@ -24,8 +25,8 @@ import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_EXIT import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS; import static com.android.server.vcn.VcnTestUtils.setupSystemService; -import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.PRIORITY_ANY; -import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.calculatePriorityClass; +import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.PRIORITY_FALLBACK; +import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.PRIORITY_INVALID; import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesCellPriorityRule; import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesPriorityRule; import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesWifiPriorityRule; @@ -48,6 +49,7 @@ import android.net.TelephonyNetworkSpecifier; import android.net.vcn.VcnCellUnderlyingNetworkTemplate; import android.net.vcn.VcnGatewayConnectionConfig; import android.net.vcn.VcnManager; +import android.net.vcn.VcnUnderlyingNetworkTemplate; import android.net.vcn.VcnWifiUnderlyingNetworkTemplate; import android.os.ParcelUuid; import android.os.PersistableBundle; @@ -64,6 +66,8 @@ import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.Collections; +import java.util.List; import java.util.Set; import java.util.UUID; @@ -91,6 +95,7 @@ public class NetworkPriorityClassifierTest { private static final NetworkCapabilities WIFI_NETWORK_CAPABILITIES = new NetworkCapabilities.Builder() .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) .setSignalStrength(WIFI_RSSI) .setSsid(SSID) .setLinkUpstreamBandwidthKbps(LINK_UPSTREAM_BANDWIDTH_KBPS) @@ -102,6 +107,7 @@ public class NetworkPriorityClassifierTest { private static final NetworkCapabilities CELL_NETWORK_CAPABILITIES = new NetworkCapabilities.Builder() .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN) .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) .setSubscriptionIds(Set.of(SUB_ID)) .setNetworkSpecifier(TEL_NETWORK_SPECIFIER) @@ -135,25 +141,35 @@ public class NetworkPriorityClassifierTest { false /* isInTestMode */)); doNothing().when(mVcnContext).ensureRunningOnLooperThread(); - mWifiNetworkRecord = - new UnderlyingNetworkRecord( - mNetwork, - WIFI_NETWORK_CAPABILITIES, - LINK_PROPERTIES, - false /* isBlocked */); - - mCellNetworkRecord = - new UnderlyingNetworkRecord( - mNetwork, - CELL_NETWORK_CAPABILITIES, - LINK_PROPERTIES, - false /* isBlocked */); - setupSystemService( mockContext, mTelephonyManager, Context.TELEPHONY_SERVICE, TelephonyManager.class); when(mTelephonyManager.createForSubscriptionId(SUB_ID)).thenReturn(mTelephonyManager); when(mTelephonyManager.getNetworkOperator()).thenReturn(PLMN_ID); when(mTelephonyManager.getSimSpecificCarrierId()).thenReturn(CARRIER_ID); + + mWifiNetworkRecord = + getTestNetworkRecord( + WIFI_NETWORK_CAPABILITIES, + VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES); + mCellNetworkRecord = + getTestNetworkRecord( + CELL_NETWORK_CAPABILITIES, + VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES); + } + + private UnderlyingNetworkRecord getTestNetworkRecord( + NetworkCapabilities nc, List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates) { + return new UnderlyingNetworkRecord( + mNetwork, + nc, + LINK_PROPERTIES, + false /* isBlocked */, + mVcnContext, + underlyingNetworkTemplates, + SUB_GROUP, + mSubscriptionSnapshot, + null /* currentlySelected */, + null /* carrierConfig */); } @Test @@ -490,37 +506,74 @@ public class NetworkPriorityClassifierTest { mSubscriptionSnapshot)); } - private void verifyCalculatePriorityClass( - UnderlyingNetworkRecord networkRecord, int expectedIndex) { - final int priorityIndex = - calculatePriorityClass( + private void verifyMatchCellWithRequiredCapabilities( + VcnCellUnderlyingNetworkTemplate template, boolean expectMatch) { + assertEquals( + expectMatch, + checkMatchesPriorityRule( mVcnContext, - networkRecord, - VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES, + template, + mCellNetworkRecord, SUB_GROUP, mSubscriptionSnapshot, null /* currentlySelected */, - null /* carrierConfig */); + null /* carrierConfig */)); + } - assertEquals(expectedIndex, priorityIndex); + @Test + public void testMatchCell() { + final VcnCellUnderlyingNetworkTemplate template = + getCellNetworkPriorityBuilder().setInternet(MATCH_REQUIRED).build(); + verifyMatchCellWithRequiredCapabilities(template, true /* expectMatch */); } @Test - public void testCalculatePriorityClass() throws Exception { - verifyCalculatePriorityClass(mCellNetworkRecord, 2); + public void testMatchCellFail_RequiredCapabilitiesMissing() { + final VcnCellUnderlyingNetworkTemplate template = + getCellNetworkPriorityBuilder().setCbs(MATCH_REQUIRED).build(); + verifyMatchCellWithRequiredCapabilities(template, false /* expectMatch */); } @Test - public void testCalculatePriorityClassFailToMatchAny() throws Exception { - final NetworkCapabilities nc = + public void testMatchCellFail_ForbiddenCapabilitiesFound() { + final VcnCellUnderlyingNetworkTemplate template = + getCellNetworkPriorityBuilder().setDun(MATCH_FORBIDDEN).build(); + verifyMatchCellWithRequiredCapabilities(template, false /* expectMatch */); + } + + @Test + public void testCalculatePriorityClass() throws Exception { + assertEquals(2, mCellNetworkRecord.priorityClass); + } + + private void checkCalculatePriorityClassFailToMatchAny( + boolean hasInternet, int expectedPriorityClass) throws Exception { + final List<VcnUnderlyingNetworkTemplate> templatesRequireDun = + Collections.singletonList( + new VcnCellUnderlyingNetworkTemplate.Builder() + .setDun(MATCH_REQUIRED) + .build()); + + final NetworkCapabilities.Builder ncBuilder = new NetworkCapabilities.Builder() - .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) - .setSignalStrength(WIFI_RSSI_LOW) - .setSsid(SSID) - .build(); - final UnderlyingNetworkRecord wifiNetworkRecord = - new UnderlyingNetworkRecord(mNetwork, nc, LINK_PROPERTIES, false /* isBlocked */); + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); + if (hasInternet) { + ncBuilder.addCapability(NET_CAPABILITY_INTERNET); + } + + final UnderlyingNetworkRecord nonDunNetworkRecord = + getTestNetworkRecord(ncBuilder.build(), templatesRequireDun); + + assertEquals(expectedPriorityClass, nonDunNetworkRecord.priorityClass); + } - verifyCalculatePriorityClass(wifiNetworkRecord, PRIORITY_ANY); + @Test + public void testCalculatePriorityClassFailToMatchAny_InternetNetwork() throws Exception { + checkCalculatePriorityClassFailToMatchAny(true /* hasInternet */, PRIORITY_FALLBACK); + } + + @Test + public void testCalculatePriorityClassFailToMatchAny_NonInternetNetwork() throws Exception { + checkCalculatePriorityClassFailToMatchAny(false /* hasInternet */, PRIORITY_INVALID); } } diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java index fad9669911bb..2941fdea20bb 100644 --- a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java +++ b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java @@ -16,18 +16,29 @@ package com.android.server.vcn.routeselection; +import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS; +import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN; +import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; +import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS; +import static android.net.vcn.VcnCellUnderlyingNetworkTemplate.MATCH_ANY; +import static android.net.vcn.VcnCellUnderlyingNetworkTemplate.MATCH_FORBIDDEN; +import static android.net.vcn.VcnCellUnderlyingNetworkTemplate.MATCH_REQUIRED; + import static com.android.server.vcn.VcnTestUtils.setupSystemService; import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT; import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.WIFI_EXIT_RSSI_THRESHOLD_DEFAULT; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; @@ -42,7 +53,10 @@ import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.net.TelephonyNetworkSpecifier; +import android.net.vcn.VcnCellUnderlyingNetworkTemplate; +import android.net.vcn.VcnCellUnderlyingNetworkTemplateTest; import android.net.vcn.VcnGatewayConnectionConfigTest; +import android.net.vcn.VcnUnderlyingNetworkTemplate; import android.os.ParcelUuid; import android.os.test.TestLooper; import android.telephony.CarrierConfigManager; @@ -64,7 +78,10 @@ import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.Set; import java.util.UUID; @@ -95,11 +112,39 @@ public class UnderlyingNetworkControllerTest { .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) .build(); + private static final NetworkCapabilities DUN_NETWORK_CAPABILITIES = + new NetworkCapabilities.Builder() + .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN) + .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED) + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) + .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED) + .build(); + + private static final NetworkCapabilities CBS_NETWORK_CAPABILITIES = + new NetworkCapabilities.Builder() + .addCapability(NetworkCapabilities.NET_CAPABILITY_CBS) + .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED) + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) + .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED) + .build(); + private static final LinkProperties INITIAL_LINK_PROPERTIES = getLinkPropertiesWithName("initial_iface"); private static final LinkProperties UPDATED_LINK_PROPERTIES = getLinkPropertiesWithName("updated_iface"); + private static final VcnCellUnderlyingNetworkTemplate CELL_TEMPLATE_DUN = + new VcnCellUnderlyingNetworkTemplate.Builder() + .setInternet(MATCH_ANY) + .setDun(MATCH_REQUIRED) + .build(); + + private static final VcnCellUnderlyingNetworkTemplate CELL_TEMPLATE_CBS = + new VcnCellUnderlyingNetworkTemplate.Builder() + .setInternet(MATCH_ANY) + .setCbs(MATCH_REQUIRED) + .build(); + @Mock private Context mContext; @Mock private VcnNetworkProvider mVcnNetworkProvider; @Mock private ConnectivityManager mConnectivityManager; @@ -201,6 +246,107 @@ public class UnderlyingNetworkControllerTest { any()); } + private void verifyRequestBackgroundNetwork( + ConnectivityManager cm, + int expectedSubId, + Set<Integer> expectedRequiredCaps, + Set<Integer> expectedForbiddenCaps) { + verify(cm) + .requestBackgroundNetwork( + eq( + getCellRequestForSubId( + expectedSubId, + expectedRequiredCaps, + expectedForbiddenCaps)), + any(NetworkBringupCallback.class), + any()); + } + + @Test + public void testNetworkCallbacksRegisteredOnStartupForNonInternetCapabilities() { + final ConnectivityManager cm = mock(ConnectivityManager.class); + setupSystemService(mContext, cm, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class); + + // Build network templates + final List<VcnUnderlyingNetworkTemplate> networkTemplates = new ArrayList(); + + networkTemplates.add( + VcnCellUnderlyingNetworkTemplateTest.getTestNetworkTemplateBuilder() + .setDun(MATCH_REQUIRED) + .setInternet(MATCH_ANY) + .build()); + + networkTemplates.add( + VcnCellUnderlyingNetworkTemplateTest.getTestNetworkTemplateBuilder() + .setMms(MATCH_REQUIRED) + .setCbs(MATCH_FORBIDDEN) + .setInternet(MATCH_ANY) + .build()); + + // Start UnderlyingNetworkController + new UnderlyingNetworkController( + mVcnContext, + VcnGatewayConnectionConfigTest.buildTestConfig(networkTemplates), + SUB_GROUP, + mSubscriptionSnapshot, + mNetworkControllerCb); + + // Verifications + for (final int subId : INITIAL_SUB_IDS) { + verifyRequestBackgroundNetwork( + cm, + subId, + Collections.singleton(NET_CAPABILITY_INTERNET), + Collections.emptySet()); + verifyRequestBackgroundNetwork( + cm, subId, Collections.singleton(NET_CAPABILITY_DUN), Collections.emptySet()); + verifyRequestBackgroundNetwork( + cm, + subId, + Collections.singleton(NET_CAPABILITY_MMS), + Collections.singleton(NET_CAPABILITY_CBS)); + } + } + + @Test + public void testNetworkCallbacksRegisteredOnStartupWithDedupedtCapabilities() { + final ConnectivityManager cm = mock(ConnectivityManager.class); + setupSystemService(mContext, cm, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class); + + // Build network templates + final List<VcnUnderlyingNetworkTemplate> networkTemplates = new ArrayList(); + final VcnCellUnderlyingNetworkTemplate.Builder builder = + new VcnCellUnderlyingNetworkTemplate.Builder() + .setMms(MATCH_REQUIRED) + .setCbs(MATCH_FORBIDDEN) + .setInternet(MATCH_ANY); + + networkTemplates.add(builder.setMetered(MATCH_REQUIRED).build()); + networkTemplates.add(builder.setMetered(MATCH_FORBIDDEN).build()); + + // Start UnderlyingNetworkController + new UnderlyingNetworkController( + mVcnContext, + VcnGatewayConnectionConfigTest.buildTestConfig(networkTemplates), + SUB_GROUP, + mSubscriptionSnapshot, + mNetworkControllerCb); + + // Verifications + for (final int subId : INITIAL_SUB_IDS) { + verifyRequestBackgroundNetwork( + cm, + subId, + Collections.singleton(NET_CAPABILITY_INTERNET), + Collections.emptySet()); + verifyRequestBackgroundNetwork( + cm, + subId, + Collections.singleton(NET_CAPABILITY_MMS), + Collections.singleton(NET_CAPABILITY_CBS)); + } + } + private void verifyNetworkRequestsRegistered(Set<Integer> expectedSubIds) { verify(mConnectivityManager) .requestBackgroundNetwork( @@ -210,8 +356,13 @@ public class UnderlyingNetworkControllerTest { for (final int subId : expectedSubIds) { verify(mConnectivityManager) .requestBackgroundNetwork( - eq(getCellRequestForSubId(subId)), - any(NetworkBringupCallback.class), any()); + eq( + getCellRequestForSubId( + subId, + Collections.singleton(NET_CAPABILITY_INTERNET), + Collections.emptySet())), + any(NetworkBringupCallback.class), + any()); } verify(mConnectivityManager) @@ -253,6 +404,7 @@ public class UnderlyingNetworkControllerTest { private NetworkRequest getWifiRequest(Set<Integer> netCapsSubIds) { return getExpectedRequestBase() .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) .setSubscriptionIds(netCapsSubIds) .build(); } @@ -261,6 +413,7 @@ public class UnderlyingNetworkControllerTest { // TODO (b/187991063): Add tests for carrier-config based thresholds return getExpectedRequestBase() .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) .setSubscriptionIds(netCapsSubIds) .setSignalStrength(WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT) .build(); @@ -270,16 +423,27 @@ public class UnderlyingNetworkControllerTest { // TODO (b/187991063): Add tests for carrier-config based thresholds return getExpectedRequestBase() .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) .setSubscriptionIds(netCapsSubIds) .setSignalStrength(WIFI_EXIT_RSSI_THRESHOLD_DEFAULT) .build(); } - private NetworkRequest getCellRequestForSubId(int subId) { - return getExpectedRequestBase() - .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) - .setNetworkSpecifier(new TelephonyNetworkSpecifier(subId)) - .build(); + private NetworkRequest getCellRequestForSubId( + int subId, Set<Integer> requiredCaps, Set<Integer> forbiddenCaps) { + final NetworkRequest.Builder nqBuilder = + getExpectedRequestBase() + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) + .setNetworkSpecifier(new TelephonyNetworkSpecifier(subId)); + + for (int cap : requiredCaps) { + nqBuilder.addCapability(cap); + } + for (int cap : forbiddenCaps) { + nqBuilder.addForbiddenCapability(cap); + } + + return nqBuilder.build(); } private NetworkRequest getRouteSelectionRequest(Set<Integer> netCapsSubIds) { @@ -301,7 +465,6 @@ public class UnderlyingNetworkControllerTest { private NetworkRequest.Builder getExpectedRequestBase() { final NetworkRequest.Builder builder = new NetworkRequest.Builder() - .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED) .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED); @@ -321,16 +484,30 @@ public class UnderlyingNetworkControllerTest { .unregisterNetworkCallback(any(UnderlyingNetworkListener.class)); } + private static UnderlyingNetworkRecord getTestNetworkRecord( + Network network, + NetworkCapabilities networkCapabilities, + LinkProperties linkProperties, + boolean isBlocked) { + return new UnderlyingNetworkRecord( + network, + networkCapabilities, + linkProperties, + isBlocked, + false /* isSelected */, + 0 /* priorityClass */); + } + @Test public void testUnderlyingNetworkRecordEquals() { UnderlyingNetworkRecord recordA = - new UnderlyingNetworkRecord( + getTestNetworkRecord( mNetwork, INITIAL_NETWORK_CAPABILITIES, INITIAL_LINK_PROPERTIES, false /* isBlocked */); UnderlyingNetworkRecord recordB = - new UnderlyingNetworkRecord( + getTestNetworkRecord( mNetwork, INITIAL_NETWORK_CAPABILITIES, INITIAL_LINK_PROPERTIES, @@ -338,12 +515,24 @@ public class UnderlyingNetworkControllerTest { UnderlyingNetworkRecord recordC = new UnderlyingNetworkRecord( mNetwork, + INITIAL_NETWORK_CAPABILITIES, + INITIAL_LINK_PROPERTIES, + false /* isBlocked */, + true /* isSelected */, + -1 /* priorityClass */); + UnderlyingNetworkRecord recordD = + getTestNetworkRecord( + mNetwork, UPDATED_NETWORK_CAPABILITIES, UPDATED_LINK_PROPERTIES, false /* isBlocked */); assertEquals(recordA, recordB); - assertNotEquals(recordA, recordC); + assertEquals(recordA, recordC); + assertNotEquals(recordA, recordD); + + assertTrue(UnderlyingNetworkRecord.isEqualIncludingPriorities(recordA, recordB)); + assertFalse(UnderlyingNetworkRecord.isEqualIncludingPriorities(recordA, recordC)); } @Test @@ -366,6 +555,10 @@ public class UnderlyingNetworkControllerTest { .build(); } + private void verifyOnSelectedUnderlyingNetworkChanged(UnderlyingNetworkRecord expectedRecord) { + verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord)); + } + private UnderlyingNetworkListener verifyRegistrationOnAvailableAndGetCallback( NetworkCapabilities networkCapabilities) { verify(mConnectivityManager) @@ -384,12 +577,12 @@ public class UnderlyingNetworkControllerTest { cb.onBlockedStatusChanged(mNetwork, false /* isFalse */); UnderlyingNetworkRecord expectedRecord = - new UnderlyingNetworkRecord( + getTestNetworkRecord( mNetwork, responseNetworkCaps, INITIAL_LINK_PROPERTIES, false /* isBlocked */); - verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord)); + verifyOnSelectedUnderlyingNetworkChanged(expectedRecord); return cb; } @@ -402,12 +595,12 @@ public class UnderlyingNetworkControllerTest { cb.onCapabilitiesChanged(mNetwork, responseNetworkCaps); UnderlyingNetworkRecord expectedRecord = - new UnderlyingNetworkRecord( + getTestNetworkRecord( mNetwork, responseNetworkCaps, INITIAL_LINK_PROPERTIES, false /* isBlocked */); - verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord)); + verifyOnSelectedUnderlyingNetworkChanged(expectedRecord); } @Test @@ -417,12 +610,12 @@ public class UnderlyingNetworkControllerTest { cb.onLinkPropertiesChanged(mNetwork, UPDATED_LINK_PROPERTIES); UnderlyingNetworkRecord expectedRecord = - new UnderlyingNetworkRecord( + getTestNetworkRecord( mNetwork, buildResponseNwCaps(INITIAL_NETWORK_CAPABILITIES, INITIAL_SUB_IDS), UPDATED_LINK_PROPERTIES, false /* isBlocked */); - verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord)); + verifyOnSelectedUnderlyingNetworkChanged(expectedRecord); } @Test @@ -434,18 +627,16 @@ public class UnderlyingNetworkControllerTest { cb.onCapabilitiesChanged(mNetwork, responseNetworkCaps); UnderlyingNetworkRecord expectedRecord = - new UnderlyingNetworkRecord( + getTestNetworkRecord( mNetwork, responseNetworkCaps, INITIAL_LINK_PROPERTIES, false /* isBlocked */); - verify(mNetworkControllerCb, times(1)) - .onSelectedUnderlyingNetworkChanged(eq(expectedRecord)); + verifyOnSelectedUnderlyingNetworkChanged(expectedRecord); // onSelectedUnderlyingNetworkChanged() won't be fired twice if network capabilities doesn't // change. cb.onCapabilitiesChanged(mNetwork, responseNetworkCaps); - verify(mNetworkControllerCb, times(1)) - .onSelectedUnderlyingNetworkChanged(eq(expectedRecord)); + verifyOnSelectedUnderlyingNetworkChanged(expectedRecord); } @Test @@ -458,18 +649,16 @@ public class UnderlyingNetworkControllerTest { cb.onCapabilitiesChanged(mNetwork, responseNetworkCaps); UnderlyingNetworkRecord expectedRecord = - new UnderlyingNetworkRecord( + getTestNetworkRecord( mNetwork, responseNetworkCaps, INITIAL_LINK_PROPERTIES, false /* isBlocked */); - verify(mNetworkControllerCb, times(1)) - .onSelectedUnderlyingNetworkChanged(eq(expectedRecord)); + verifyOnSelectedUnderlyingNetworkChanged(expectedRecord); // onSelectedUnderlyingNetworkChanged() won't be fired twice if network capabilities doesn't // change. cb.onCapabilitiesChanged(mNetwork, responseNetworkCaps); - verify(mNetworkControllerCb, times(1)) - .onSelectedUnderlyingNetworkChanged(eq(expectedRecord)); + verifyOnSelectedUnderlyingNetworkChanged(expectedRecord); } @Test @@ -478,13 +667,7 @@ public class UnderlyingNetworkControllerTest { cb.onBlockedStatusChanged(mNetwork, true /* isBlocked */); - UnderlyingNetworkRecord expectedRecord = - new UnderlyingNetworkRecord( - mNetwork, - buildResponseNwCaps(INITIAL_NETWORK_CAPABILITIES, INITIAL_SUB_IDS), - INITIAL_LINK_PROPERTIES, - true /* isBlocked */); - verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord)); + verifyOnSelectedUnderlyingNetworkChanged(null); } @Test @@ -520,5 +703,132 @@ public class UnderlyingNetworkControllerTest { verify(mNetworkControllerCb, times(1)).onSelectedUnderlyingNetworkChanged(any()); } - // TODO (b/187991063): Add tests for network prioritization + private UnderlyingNetworkListener setupControllerAndGetNetworkListener( + List<VcnUnderlyingNetworkTemplate> networkTemplates) { + final ConnectivityManager cm = mock(ConnectivityManager.class); + setupSystemService(mContext, cm, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class); + + new UnderlyingNetworkController( + mVcnContext, + VcnGatewayConnectionConfigTest.buildTestConfig(networkTemplates), + SUB_GROUP, + mSubscriptionSnapshot, + mNetworkControllerCb); + + verify(cm) + .registerNetworkCallback( + eq(getRouteSelectionRequest(INITIAL_SUB_IDS)), + mUnderlyingNetworkListenerCaptor.capture(), + any()); + + return mUnderlyingNetworkListenerCaptor.getValue(); + } + + private UnderlyingNetworkRecord bringupNetworkAndGetRecord( + UnderlyingNetworkListener cb, + NetworkCapabilities requestNetworkCaps, + List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, + UnderlyingNetworkRecord currentlySelected) { + final Network network = mock(Network.class); + final NetworkCapabilities responseNetworkCaps = + buildResponseNwCaps(requestNetworkCaps, INITIAL_SUB_IDS); + + cb.onAvailable(network); + cb.onCapabilitiesChanged(network, responseNetworkCaps); + cb.onLinkPropertiesChanged(network, INITIAL_LINK_PROPERTIES); + cb.onBlockedStatusChanged(network, false /* isFalse */); + return new UnderlyingNetworkRecord( + network, + responseNetworkCaps, + INITIAL_LINK_PROPERTIES, + false /* isBlocked */, + mVcnContext, + underlyingNetworkTemplates, + SUB_GROUP, + mSubscriptionSnapshot, + currentlySelected, + null /* carrierConfig */); + } + + @Test + public void testSelectMorePreferredNetwork() { + final List<VcnUnderlyingNetworkTemplate> networkTemplates = new ArrayList(); + networkTemplates.add(CELL_TEMPLATE_DUN); + networkTemplates.add(CELL_TEMPLATE_CBS); + + UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates); + + // Bring up CBS network + final UnderlyingNetworkRecord cbsNetworkRecord = + bringupNetworkAndGetRecord( + cb, + CBS_NETWORK_CAPABILITIES, + networkTemplates, + null /* currentlySelected */); + verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(cbsNetworkRecord)); + + // Bring up DUN network + final UnderlyingNetworkRecord dunNetworkRecord = + bringupNetworkAndGetRecord( + cb, DUN_NETWORK_CAPABILITIES, networkTemplates, cbsNetworkRecord); + verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(dunNetworkRecord)); + } + + @Test + public void testNeverSelectLessPreferredNetwork() { + final List<VcnUnderlyingNetworkTemplate> networkTemplates = new ArrayList(); + networkTemplates.add(CELL_TEMPLATE_DUN); + networkTemplates.add(CELL_TEMPLATE_CBS); + + UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates); + + // Bring up DUN network + final UnderlyingNetworkRecord dunNetworkRecord = + bringupNetworkAndGetRecord( + cb, + DUN_NETWORK_CAPABILITIES, + networkTemplates, + null /* currentlySelected */); + verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(dunNetworkRecord)); + + // Bring up CBS network + final UnderlyingNetworkRecord cbsNetworkRecord = + bringupNetworkAndGetRecord( + cb, CBS_NETWORK_CAPABILITIES, networkTemplates, dunNetworkRecord); + verify(mNetworkControllerCb, never()) + .onSelectedUnderlyingNetworkChanged(eq(cbsNetworkRecord)); + } + + @Test + public void testFailtoMatchTemplateAndFallBackToInternetNetwork() { + final List<VcnUnderlyingNetworkTemplate> networkTemplates = new ArrayList(); + + networkTemplates.add( + new VcnCellUnderlyingNetworkTemplate.Builder().setDun(MATCH_REQUIRED).build()); + UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates); + + // Bring up an Internet network without DUN capability + final UnderlyingNetworkRecord networkRecord = + bringupNetworkAndGetRecord( + cb, + INITIAL_NETWORK_CAPABILITIES, + networkTemplates, + null /* currentlySelected */); + verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(networkRecord)); + } + + @Test + public void testFailtoMatchTemplateAndNeverFallBackToNonInternetNetwork() { + final List<VcnUnderlyingNetworkTemplate> networkTemplates = new ArrayList(); + + networkTemplates.add( + new VcnCellUnderlyingNetworkTemplate.Builder().setDun(MATCH_REQUIRED).build()); + UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates); + + bringupNetworkAndGetRecord( + cb, CBS_NETWORK_CAPABILITIES, networkTemplates, null /* currentlySelected */); + + verify(mNetworkControllerCb, never()) + .onSelectedUnderlyingNetworkChanged(any(UnderlyingNetworkRecord.class)); + } } |