fix: 引入Settings的Module

This commit is contained in:
2024-12-10 14:57:24 +08:00
parent ad8fc8731d
commit df105485bd
6934 changed files with 896168 additions and 2 deletions

View File

@@ -0,0 +1,34 @@
package {
default_team: "trendy_team_android_settings_app",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "packages_apps_Settings_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["packages_apps_Settings_license"],
}
android_test {
name: "AnomalyTester",
certificate: "platform",
libs: ["android.test.runner"],
static_libs: [
"androidx.test.rules",
"mockito-target",
"androidx.test.uiautomator_uiautomator",
"truth",
],
srcs: ["**/*.java"],
optimize: {
enabled: false,
},
platform_apis: true,
instrumentation_for: "Settings",
}

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.settings.anomaly.tester">
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
<application
android:allowBackup="false"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@android:style/Theme.Material.Light.DarkActionBar">
<uses-library android:name="android.test.runner" />
<activity
android:name=".AnomalyActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".service.AnomalyService"
android:exported="false"/>
</application>
<instrumentation
android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.settings"
android:label="Settings Test Cases">
</instrumentation>
</manifest>

View File

@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="vertical">
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="4"
android:padding="4dp"
android:textSize="16sp">
<TextView
android:id="@+id/displayText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="100000"/>
</ScrollView>
<View
android:layout_width="match_parent"
android:layout_height="2dp"
android:background="#444444"/>
<ScrollView
android:layout_height="0dp"
android:layout_width="match_parent"
android:layout_weight="6"
android:padding="5dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<include layout="@layout/bluetooth_anomaly"/>
<include layout="@layout/wakelock_anomaly"/>
</LinearLayout>
</ScrollView>
</LinearLayout>

View File

@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="6dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Bluetooth Anomaly"
android:textSize="16sp"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:id="@+id/bluetooth_threshold"
android:layout_width="0dp"
android:layout_weight="3"
android:layout_height="wrap_content"
android:hint="Threshold(ms)"
android:text="3000"
android:inputType="number"/>
<EditText
android:id="@+id/bluetooth_run_time"
android:layout_width="0dp"
android:layout_weight="3"
android:layout_height="wrap_content"
android:hint="Run time(ms)"
android:text="6000"
android:inputType="number"/>
<Button
android:id="@+id/bluetooth_button"
android:layout_width="0dp"
android:layout_weight="2"
android:layout_height="wrap_content"
android:text="START"
android:onClick="startBluetoothAnomaly"/>
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="6dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Wakelock Anomaly"
android:textSize="16sp"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:id="@+id/wakelock_threshold"
android:layout_width="0dp"
android:layout_weight="3"
android:layout_height="wrap_content"
android:hint="Threshold(ms)"
android:text="3000"
android:inputType="number"/>
<EditText
android:id="@+id/wakelock_run_time"
android:layout_width="0dp"
android:layout_weight="3"
android:layout_height="wrap_content"
android:hint="Run time(ms)"
android:text="6000"
android:inputType="number"/>
<Button
android:id="@+id/wakelock_button"
android:layout_width="0dp"
android:layout_weight="2"
android:layout_height="wrap_content"
android:text="START"
android:onClick="startWakelockAnomaly"/>
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<string name="app_name" translatable="false">AnomalyTester</string>
</resources>

View File

@@ -0,0 +1,133 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package com.android.settings.anomaly.tester;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.ResultReceiver;
import android.provider.Settings;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.android.settings.anomaly.tester.service.AnomalyService;
import com.android.settings.anomaly.tester.utils.AnomalyActions;
import com.android.settings.anomaly.tester.utils.AnomalyPolicyBuilder;
/**
* Main activity to control and start anomaly
*/
public class AnomalyActivity extends Activity {
private static final String TAG = AnomalyActivity.class.getSimpleName();
public static final String KEY_TARGET_BUTTON = "target_button";
private AnomalyResultReceiver mResultReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mResultReceiver = new AnomalyResultReceiver(new Handler());
}
public void startBluetoothAnomaly(View view) {
try {
// Enable anomaly detection and change the threshold
final String config = new AnomalyPolicyBuilder()
.addPolicy(AnomalyPolicyBuilder.KEY_ANOMALY_DETECTION_ENABLED, true)
.addPolicy(AnomalyPolicyBuilder.KEY_BLUETOOTH_SCAN_DETECTION_ENABLED, true)
.addPolicy(AnomalyPolicyBuilder.KEY_BLUETOOTH_SCAN_THRESHOLD,
getValueFromEditText(R.id.bluetooth_threshold))
.build();
Settings.Global.putString(getContentResolver(),
Settings.Global.ANOMALY_DETECTION_CONSTANTS, config);
// Start the anomaly service
Intent intent = new Intent(this, AnomalyService.class);
intent.putExtra(AnomalyActions.KEY_ACTION, AnomalyActions.ACTION_BLE_SCAN_UNOPTIMIZED);
intent.putExtra(AnomalyActions.KEY_DURATION_MS,
getValueFromEditText(R.id.bluetooth_run_time));
intent.putExtra(AnomalyActions.KEY_RESULT_RECEIVER, mResultReceiver);
intent.putExtra(KEY_TARGET_BUTTON, view.getId());
startService(intent);
view.setEnabled(false);
} catch (NumberFormatException e) {
Toast.makeText(getApplicationContext(), e.toString(), Toast.LENGTH_SHORT).show();
}
}
public void startWakelockAnomaly(View view) {
try {
// Enable anomaly detection and change the threshold
final String config = new AnomalyPolicyBuilder()
.addPolicy(AnomalyPolicyBuilder.KEY_ANOMALY_DETECTION_ENABLED, true)
.addPolicy(AnomalyPolicyBuilder.KEY_WAKELOCK_DETECTION_ENABLED, true)
.addPolicy(AnomalyPolicyBuilder.KEY_WAKELOCK_THRESHOLD,
getValueFromEditText(R.id.wakelock_threshold))
.build();
Settings.Global.putString(getContentResolver(),
Settings.Global.ANOMALY_DETECTION_CONSTANTS,
config);
// Start the anomaly service
Intent intent = new Intent(this, AnomalyService.class);
intent.putExtra(AnomalyActions.KEY_ACTION, AnomalyActions.ACTION_WAKE_LOCK);
intent.putExtra(AnomalyActions.KEY_DURATION_MS,
getValueFromEditText(R.id.wakelock_run_time));
intent.putExtra(AnomalyActions.KEY_RESULT_RECEIVER, mResultReceiver);
intent.putExtra(KEY_TARGET_BUTTON, view.getId());
startService(intent);
view.setEnabled(false);
} catch (NumberFormatException e) {
Toast.makeText(getApplicationContext(), e.toString(), Toast.LENGTH_SHORT).show();
}
}
private long getValueFromEditText(final int id) throws NumberFormatException {
final EditText editText = findViewById(id);
if (editText != null) {
final long value = Long.parseLong(editText.getText().toString());
if (value > 0) {
return value;
}
}
throw new NumberFormatException("Number should be positive");
}
private class AnomalyResultReceiver extends ResultReceiver {
public AnomalyResultReceiver(Handler handler) {
super(handler);
}
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
super.onReceiveResult(resultCode, resultData);
final Button button = findViewById(resultData.getInt(KEY_TARGET_BUTTON));
if (button != null) {
button.setEnabled(true);
}
}
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.anomaly.tester.service;
import android.annotation.Nullable;
import android.app.IntentService;
import android.content.Intent;
import android.os.ResultReceiver;
import com.android.settings.anomaly.tester.utils.AnomalyActions;
/**
* Service to run the anomaly action
*/
public class AnomalyService extends IntentService {
private static final String TAG = AnomalyService.class.getSimpleName();
public AnomalyService() {
super(AnomalyService.class.getSimpleName());
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
final String action = intent.getStringExtra(AnomalyActions.KEY_ACTION);
final long durationMs = intent.getLongExtra(AnomalyActions.KEY_DURATION_MS, 0);
final ResultReceiver resultReceiver = intent.getParcelableExtra(
AnomalyActions.KEY_RESULT_RECEIVER);
AnomalyActions.doAction(this, action, durationMs);
if (resultReceiver != null) {
resultReceiver.send(0 /* resultCode */, intent.getExtras());
}
}
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.anomaly.tester.utils;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.Context;
import android.os.PowerManager;
import android.util.Log;
import java.util.List;
/**
* Actions to generate anomaly.
*/
public class AnomalyActions {
private static final String TAG = AnomalyActions.class.getSimpleName();
public static final String KEY_ACTION = "action";
public static final String KEY_DURATION_MS = "duration_ms";
public static final String KEY_RESULT_RECEIVER = "result_receiver";
public static final String ACTION_BLE_SCAN_UNOPTIMIZED = "action.ble_scan_unoptimized";
public static final String ACTION_WAKE_LOCK = "action.wake_lock";
public static void doAction(Context ctx, String actionCode, long durationMs) {
if (actionCode == null) {
Log.e(TAG, "Intent was missing action.");
return;
}
switch (actionCode) {
case ACTION_BLE_SCAN_UNOPTIMIZED:
doUnoptimizedBleScan(ctx, durationMs);
break;
case ACTION_WAKE_LOCK:
doHoldWakelock(ctx, durationMs);
default:
Log.e(TAG, "Intent had invalid action");
}
}
private static void doUnoptimizedBleScan(Context ctx, long durationMs) {
ScanSettings scanSettings = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
// perform ble scanning
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled() ) {
Log.e(TAG, "Device does not support Bluetooth or Bluetooth not enabled");
return;
}
BluetoothLeScanner bleScanner = bluetoothAdapter.getBluetoothLeScanner();
if (bleScanner == null) {
Log.e(TAG, "Cannot access BLE scanner");
return;
}
ScanCallback scanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
Log.v(TAG, "called onScanResult");
}
@Override
public void onScanFailed(int errorCode) {
Log.v(TAG, "called onScanFailed");
}
@Override
public void onBatchScanResults(List<ScanResult> results) {
Log.v(TAG, "called onBatchScanResults");
}
};
bleScanner.startScan(null, scanSettings, scanCallback);
try {
Thread.sleep(durationMs);
} catch (InterruptedException e) {
Log.e(TAG, "Thread couldn't sleep for " + durationMs, e);
}
bleScanner.stopScan(scanCallback);
}
private static void doHoldWakelock(Context ctx, long durationMs) {
PowerManager powerManager = ctx.getSystemService(PowerManager.class);
PowerManager.WakeLock wl = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"AnomalyWakeLock");
wl.acquire();
try {
Thread.sleep(durationMs);
} catch (InterruptedException e) {
Log.e(TAG, "Thread couldn't sleep for " + durationMs, e);
}
wl.release();
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.anomaly.tester.utils;
import java.util.HashMap;
import java.util.Map;
/**
* Builder to build the anomaly policy string, used in {@link android.provider.Settings.Global}
*
* @see android.provider.Settings.Global#ANOMALY_DETECTION_CONSTANTS
*/
public class AnomalyPolicyBuilder {
public static final String KEY_ANOMALY_DETECTION_ENABLED = "anomaly_detection_enabled";
public static final String KEY_WAKELOCK_DETECTION_ENABLED = "wakelock_enabled";
public static final String KEY_WAKEUP_ALARM_DETECTION_ENABLED = "wakeup_alarm_enabled";
public static final String KEY_BLUETOOTH_SCAN_DETECTION_ENABLED = "bluetooth_scan_enabled";
public static final String KEY_WAKELOCK_THRESHOLD = "wakelock_threshold";
public static final String KEY_WAKEUP_ALARM_THRESHOLD = "wakeup_alarm_threshold";
public static final String KEY_BLUETOOTH_SCAN_THRESHOLD = "bluetooth_scan_threshold";
public static final String DELIM = ",";
private Map<String, String> mValues;
public AnomalyPolicyBuilder() {
mValues = new HashMap<>();
}
public AnomalyPolicyBuilder addPolicy(String key, String value) {
mValues.put(key, value);
return this;
}
public AnomalyPolicyBuilder addPolicy(String key, long value) {
mValues.put(key, Long.toString(value));
return this;
}
public AnomalyPolicyBuilder addPolicy(String key, boolean value) {
mValues.put(key, value ? "true" : "false");
return this;
}
public String build() {
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> entry : mValues.entrySet()) {
sb.append(entry.getKey() + "=" + entry.getValue() + DELIM);
}
if (sb.length() != 0) {
return sb.substring(0, sb.length() - 1);
} else {
return "";
}
}
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package com.android.settings.anomaly.tests;
import static com.google.common.truth.Truth.assertWithMessage;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
import android.text.format.DateUtils;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.UiDevice;
import androidx.test.uiautomator.Until;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* Functional test for bluetooth unoptimized scanning anomaly detector
*
* @see com.android.settings.fuelgauge.anomaly.checker.BluetoothScanAnomalyDetector
*/
@RunWith(AndroidJUnit4.class)
public class BluetoothAnomalyTest {
private static final String BATTERY_INTENT = "android.intent.action.POWER_USAGE_SUMMARY";
private static final String RES_BT_EDITTEXT =
"com.android.settings.anomaly.tester:id/bluetooth_run_time";
private static final String RES_BT_BUTTON =
"com.android.settings.anomaly.tester:id/bluetooth_button";
private static final long TIME_OUT = 3000;
private UiDevice mDevice;
@Before
public void setUp() {
final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
final Context context = instrumentation.getContext();
mDevice = UiDevice.getInstance(instrumentation);
// setup environment
TestUtils.setUp(instrumentation);
// start anomaly-tester app
TestUtils.startAnomalyApp(context, mDevice);
}
@After
public void tearDown() {
TestUtils.tearDown(InstrumentationRegistry.getInstrumentation());
}
@Test
public void testBluetoothAnomaly_longScanningTime_reportAnomaly() throws InterruptedException {
// Set running time
final long durationMs = DateUtils.SECOND_IN_MILLIS * 15;
TestUtils.setEditTextWithValue(mDevice, RES_BT_EDITTEXT, durationMs);
// Click start button
TestUtils.clickButton(mDevice, RES_BT_BUTTON);
// Wait for its running
mDevice.pressHome();
TestUtils.wait(mDevice, durationMs);
// Check it in battery main page
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
instrumentation.startActivitySync(new Intent(BATTERY_INTENT));
assertWithMessage("Doesn't have bluetooth anomaly").that(
mDevice.wait(Until.findObject(By.text("AnomalyTester draining battery")),
TIME_OUT)).isNotNull();
}
@Test
public void testBluetoothAnomaly_shortScanningTime_notReport() throws InterruptedException {
// Set running time
final long durationMs = DateUtils.SECOND_IN_MILLIS;
TestUtils.setEditTextWithValue(mDevice, RES_BT_EDITTEXT, durationMs);
// Click start button
TestUtils.clickButton(mDevice, RES_BT_BUTTON);
// Wait for its running
mDevice.pressHome();
TestUtils.wait(mDevice, durationMs);
// Check it in battery main page
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
instrumentation.startActivitySync(new Intent(BATTERY_INTENT));
assertWithMessage("Shouldn't have bluetooth anomaly").that(
mDevice.wait(Until.findObject(By.text("AnomalyTester draining battery")),
TIME_OUT)).isNull();
}
}

View File

@@ -0,0 +1,92 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package com.android.settings.anomaly.tests;
import static com.google.common.truth.Truth.assertWithMessage;
import android.app.Instrumentation;
import android.app.UiAutomation;
import android.content.Context;
import android.content.Intent;
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.UiDevice;
import androidx.test.uiautomator.UiObject2;
import androidx.test.uiautomator.Until;
public class TestUtils {
private static final String PACKAGE_NAME = "com.android.settings.anomaly.tester";
private static final long TIME_OUT = 3000;
/**
* This method set up the environment for anomaly test
*
* @param instrumentation to execute command
*/
public static void setUp(Instrumentation instrumentation) {
final UiAutomation uiAutomation = instrumentation.getUiAutomation();
// pretend unplug and screen off, also reset the battery stats
uiAutomation.executeShellCommand("dumpsys battery unplug");
uiAutomation.executeShellCommand("dumpsys batterystats enable pretend-screen-off");
uiAutomation.executeShellCommand("dumpsys batterystats --reset");
}
/**
* This method cleans up all the commands in {@link #setUp(Instrumentation)}
*
* @param instrumentation to execute command
*/
public static void tearDown(Instrumentation instrumentation) {
final UiAutomation uiAutomation = instrumentation.getUiAutomation();
// reset unplug and screen-off
uiAutomation.executeShellCommand("dumpsys battery reset");
uiAutomation.executeShellCommand("dumpsys batterystats disable pretend-screen-off");
}
public static void startAnomalyApp(Context context, UiDevice uiDevice) {
final Intent intent = context.getPackageManager().getLaunchIntentForPackage(PACKAGE_NAME);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
context.startActivity(intent);
uiDevice.wait(Until.hasObject(By.pkg(PACKAGE_NAME).depth(0)), TIME_OUT);
}
/**
* Find {@link android.widget.EditText} with {@code res} and set its {@code value}
*/
public static void setEditTextWithValue(UiDevice uiDevice, String res, long value) {
final UiObject2 editText = uiDevice.findObject(By.res(res));
assertWithMessage("Cannot find editText with res: " + res).that(editText).isNotNull();
editText.setText(String.valueOf(value));
}
/**
* Find {@link android.widget.Button} with {@code res} and click it
*/
public static void clickButton(UiDevice uiDevice, String res) {
final UiObject2 button = uiDevice.findObject(By.res(res));
assertWithMessage("Cannot find button with res: " + res).that(button).isNotNull();
button.click();
}
/**
* Make {@link UiDevice} wait for {@code timeMs}
*
* @see Thread#sleep(long)
*/
public static void wait(UiDevice uiDevice, long timeMs) throws InterruptedException {
uiDevice.waitForIdle();
Thread.sleep(timeMs);
}
}

View File

@@ -0,0 +1,133 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package com.android.settings.anomaly.tests;
import static com.google.common.truth.Truth.assertWithMessage;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
import android.text.format.DateUtils;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.UiDevice;
import androidx.test.uiautomator.Until;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* Functional test for bluetooth unoptimized scanning anomaly detector
*
* @see com.android.settings.fuelgauge.anomaly.checker.BluetoothScanAnomalyDetector
*/
@RunWith(AndroidJUnit4.class)
public class WakelockAnomalyTest {
private static final String BATTERY_INTENT = "android.intent.action.POWER_USAGE_SUMMARY";
private static final String RES_WAKELOCK_EDITTEXT =
"com.android.settings.anomaly.tester:id/wakelock_run_time";
private static final String RES_WAKELOCK_BUTTON =
"com.android.settings.anomaly.tester:id/wakelock_button";
private static final long TIME_OUT = 3000;
private UiDevice mDevice;
@Before
public void setUp() {
final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
final Context context = instrumentation.getContext();
mDevice = UiDevice.getInstance(instrumentation);
// setup environment
TestUtils.setUp(instrumentation);
// start anomaly-tester app
TestUtils.startAnomalyApp(context, mDevice);
}
@After
public void tearDown() {
TestUtils.tearDown(InstrumentationRegistry.getInstrumentation());
}
@Test
public void testWakelockAnomaly_longTimeWhileRunning_report() throws InterruptedException {
// Set running time
final long durationMs = DateUtils.SECOND_IN_MILLIS * 15;
TestUtils.setEditTextWithValue(mDevice, RES_WAKELOCK_EDITTEXT, durationMs);
// Click start button
TestUtils.clickButton(mDevice, RES_WAKELOCK_BUTTON);
// Wait for its running
mDevice.pressHome();
// Sleeping time less than running time, so the app still holding wakelock when we check
TestUtils.wait(mDevice, durationMs - TIME_OUT);
// Check it in battery main page
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
instrumentation.startActivitySync(new Intent(BATTERY_INTENT));
assertWithMessage("Doesn't have wakelock anomaly").that(
mDevice.wait(Until.findObject(By.text("AnomalyTester draining battery")),
TIME_OUT)).isNotNull();
}
@Test
public void testWakelockAnomaly_shortTime_notReport() throws InterruptedException {
// Set running time
final long durationMs = DateUtils.SECOND_IN_MILLIS;
TestUtils.setEditTextWithValue(mDevice, RES_WAKELOCK_EDITTEXT, durationMs);
// Click start button
TestUtils.clickButton(mDevice, RES_WAKELOCK_BUTTON);
// Wait for its running
mDevice.pressHome();
TestUtils.wait(mDevice, durationMs);
// Check it in battery main page
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
instrumentation.startActivitySync(new Intent(BATTERY_INTENT));
assertWithMessage("Shouldn't have wakelock anomaly").that(
mDevice.wait(Until.findObject(By.text("AnomalyTester draining battery")),
TIME_OUT)).isNull();
}
@Test
public void testWakelockAnomaly_longTimeWhileNotRunning_notReport()
throws InterruptedException {
// Set running time
final long durationMs = DateUtils.SECOND_IN_MILLIS * 10;
TestUtils.setEditTextWithValue(mDevice, RES_WAKELOCK_EDITTEXT, durationMs);
// Click start button
TestUtils.clickButton(mDevice, RES_WAKELOCK_BUTTON);
// Wait for its running
mDevice.pressHome();
// Wait more time for releasing the wakelock
TestUtils.wait(mDevice, durationMs + TIME_OUT);
// Check it in battery main page
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
instrumentation.startActivitySync(new Intent(BATTERY_INTENT));
assertWithMessage("Shouldn't have wakelock anomaly").that(
mDevice.wait(Until.findObject(By.text("AnomalyTester draining battery")),
TIME_OUT)).isNull();
}
}

View File

@@ -0,0 +1,38 @@
//############################################################
// Settings Component test target. #
//############################################################
package {
default_team: "trendy_team_android_settings_app",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "packages_apps_Settings_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["packages_apps_Settings_license"],
}
android_test {
name: "SettingsComponentTests",
certificate: "platform",
privileged: true,
srcs: [
"src/**/*.java",
],
static_libs: [
"truth",
"androidx.test.core",
"androidx.test.espresso.core",
"androidx.test.espresso.intents-nodeps",
"androidx.test.runner",
"androidx.test.rules",
"androidx.test.ext.junit",
"mockito-target",
],
test_suites: ["device-tests"],
optimize: {
enabled: false,
},
instrumentation_for: "Settings",
}

View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2016 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"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
package="com.android.settings.tests.component"
android:sharedUserId="android.uid.systemui">
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
<uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application/>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.settings"
android:label="Settings Test Cases">
</instrumentation>
</manifest>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<configuration description="Runs Settings Test Cases.">
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="apct-instrumentation" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="SettingsComponentTests.apk" />
</target_preparer>
<option name="test-tag" value="SettingsComponentTests" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.settings.tests.component" />
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
<option name="hidden-api-checks" value="false"/>
</test>
</configuration>

View File

@@ -0,0 +1,3 @@
# People who can approve changes for submission
jyhsu@google.com
syaoranx@google.com

View File

@@ -0,0 +1,201 @@
/*
* Copyright (C) 2021 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.settings.biometrics;
import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_STRONG;
import static android.provider.Settings.ACTION_BIOMETRIC_ENROLL;
import static androidx.test.espresso.intent.Intents.intended;
import static androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent;
import static androidx.test.espresso.intent.matcher.IntentMatchers.hasExtra;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assume.assumeTrue;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.hardware.biometrics.SensorProperties;
import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.os.UserHandle;
import android.provider.Settings;
import androidx.test.core.app.ActivityScenario;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.espresso.intent.Intents;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.MediumTest;
import com.android.internal.widget.LockPatternChecker;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockscreenCredential;
import com.android.settings.biometrics.face.FaceEnrollIntroduction;
import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroduction;
import com.android.settings.password.ChooseLockGeneric;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settings.password.ConfirmLockPassword;
import com.android.settings.testutils.AdbUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.List;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class BiometricEnrollActivityTest {
private static final String TAG = "BiometricEnrollActivityTest";
private static final int ADB_TIMEOUT_MS = 5000;
private static final String TEST_PIN = "1234";
private final Context mContext = ApplicationProvider.getApplicationContext();
private boolean mHasFace;
private boolean mHasFingerprint;
private boolean mIsFaceStrong;
private boolean mIsFingerprintStrong;
@Before
public void setup() {
Intents.init();
final PackageManager pm = mContext.getPackageManager();
mHasFingerprint = pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT);
mHasFace = pm.hasSystemFeature(PackageManager.FEATURE_FACE);
if (mHasFace) {
final FaceManager faceManager = mContext.getSystemService(FaceManager.class);
final List<FaceSensorPropertiesInternal> faceProperties =
faceManager.getSensorPropertiesInternal();
if (!faceProperties.isEmpty()) {
final FaceSensorPropertiesInternal faceProp = faceProperties.get(0);
mIsFaceStrong = faceProp.sensorStrength == SensorProperties.STRENGTH_STRONG;
}
}
if (mHasFingerprint) {
final FingerprintManager fingerprintManager = mContext.getSystemService(
FingerprintManager.class);
final List<FingerprintSensorPropertiesInternal> fingerProperties =
fingerprintManager.getSensorPropertiesInternal();
if (!fingerProperties.isEmpty()) {
final FingerprintSensorPropertiesInternal fingerProp = fingerProperties.get(0);
mIsFingerprintStrong =
fingerProp.sensorStrength == SensorProperties.STRENGTH_STRONG;
}
}
}
@After
public void teardown() throws Exception {
Intents.release();
AdbUtils.checkStringInAdbCommandOutput(TAG, "locksettings clear --old " + TEST_PIN,
"", "", ADB_TIMEOUT_MS);
}
@Test
public void launchWithoutPin_setsPin() {
try (ActivityScenario<BiometricEnrollActivity> scenario =
ActivityScenario.launch(getIntent())) {
intended(hasComponent(ChooseLockGeneric.class.getName()));
if (mHasFace && mHasFingerprint) {
intended(hasExtra(EXTRA_KEY_FOR_BIOMETRICS, true));
} else if (mHasFace) {
intended(hasExtra(EXTRA_KEY_FOR_FACE, true));
} else if (mHasFingerprint) {
intended(hasExtra(EXTRA_KEY_FOR_FINGERPRINT, true));
}
}
}
@Test
public void launchWithPin_confirmsPin() throws Exception {
setPin();
try (ActivityScenario<BiometricEnrollActivity> scenario =
ActivityScenario.launch(getIntent())) {
intended(hasComponent(ConfirmLockPassword.InternalActivity.class.getName()));
}
}
@Test
public void launchWithPinAndPwHandle_confirmsPin() throws Exception {
assumeTrue(mHasFace || mHasFingerprint);
setPin();
final Intent intent = getIntent(true /* useInternal */);
LockPatternChecker.verifyCredential(new LockPatternUtils(mContext),
LockscreenCredential.createPin(TEST_PIN), UserHandle.myUserId(),
LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE, (response, timeoutMs) -> {
assertThat(response.containsGatekeeperPasswordHandle()).isTrue();
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE,
response.getGatekeeperPasswordHandle());
}).get();
try (ActivityScenario<BiometricEnrollActivity> scenario =
ActivityScenario.launch(intent)) {
intended(hasComponent(mHasFace && !mHasFingerprint
? FaceEnrollIntroduction.class.getName()
: FingerprintEnrollIntroduction.class.getName()));
}
}
@Test
public void launchWithStrongBiometricAllowed_doNotEnrollWeak() throws Exception {
assumeTrue(mHasFace || mHasFingerprint);
// Allow only strong biometrics
Intent intent = getIntent();
intent.putExtra(Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED, BIOMETRIC_STRONG);
try (ActivityScenario<BiometricEnrollActivity> scenario =
ActivityScenario.launch(intent)) {
intended(hasComponent(ChooseLockGeneric.class.getName()));
if (mIsFaceStrong && mIsFingerprintStrong) {
intended(hasExtra(EXTRA_KEY_FOR_BIOMETRICS, true));
} else if (mIsFaceStrong) {
intended(hasExtra(EXTRA_KEY_FOR_FACE, true));
} else if (mIsFingerprintStrong) {
intended(hasExtra(EXTRA_KEY_FOR_FINGERPRINT, true));
}
}
}
private Intent getIntent() {
return getIntent(false /* useInternal */);
}
private Intent getIntent(boolean useInternal) {
final Intent intent = new Intent(mContext, useInternal
? BiometricEnrollActivity.InternalActivity.class : BiometricEnrollActivity.class);
intent.setAction(ACTION_BIOMETRIC_ENROLL);
return intent;
}
private static void setPin() throws Exception {
assertThat(AdbUtils.checkStringInAdbCommandOutput(TAG, "locksettings set-pin " + TEST_PIN,
"Pin set to ", "'" + TEST_PIN + "'", ADB_TIMEOUT_MS)).isTrue();
}
}

View File

@@ -0,0 +1,144 @@
/*
* Copyright (C) 2021 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.settings.display.darkmode;
import static com.google.common.truth.Truth.assertThat;
import android.app.Instrumentation;
import android.app.TimePickerDialog;
import android.app.UiModeManager;
import android.content.Intent;
import android.content.res.Configuration;
import android.provider.Settings;
import android.util.Log;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.test.core.app.ActivityScenario;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.lifecycle.Stage;
import com.android.settings.testutils.CommonUtils;
import com.android.settings.testutils.UiUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.time.LocalTime;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class DarkThemeScheduleComponentTest {
private static final int DIALOG_START_TIME = 0;
private static final int DIALOG_END_TIME = 1;
/** The identifier for the positive button. */
private static final int BUTTON_POSITIVE = -1;
public final String TAG = this.getClass().getName();
private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
@Rule
public ActivityScenarioRule<com.android.settings.Settings.DarkThemeSettingsActivity> rule =
new ActivityScenarioRule<>(
new Intent(
Settings.ACTION_DARK_THEME_SETTINGS).setFlags(
Intent.FLAG_ACTIVITY_NEW_TASK));
private UiModeManager mUiModeManager;
@Before
public void setUp() {
mUiModeManager = mInstrumentation.getTargetContext().getSystemService(UiModeManager.class);
if (mUiModeManager.getNightMode() != UiModeManager.MODE_NIGHT_NO) {
mUiModeManager.setNightMode(UiModeManager.MODE_NIGHT_NO);
}
}
private void test_step_for_custom_time(int startTimeDiff, int endTimeDiff) {
ActivityScenario scenario = rule.getScenario();
scenario.onActivity(activity -> {
mUiModeManager.setNightMode(UiModeManager.MODE_NIGHT_CUSTOM);
Fragment f =
((FragmentActivity) activity).getSupportFragmentManager().getFragments().get(0);
DarkModeSettingsFragment fragment = (DarkModeSettingsFragment) f;
setCustomTime(fragment, DIALOG_START_TIME, LocalTime.now().plusMinutes(startTimeDiff));
setCustomTime(fragment, DIALOG_END_TIME, LocalTime.now().plusMinutes(endTimeDiff));
// The night mode need to reopen the screen to trigger UI change after mode change.
CommonUtils.reopenScreen();
});
// Relaunch the scenario to make sure UI apply new mode.
scenario.onActivity(activity -> {
Log.d(TAG, "Activity Recreated!");
UiUtils.waitForActivitiesInStage(2000, Stage.RESUMED);
});
}
@Test
public void test_dark_mode_in_custom_time() {
test_step_for_custom_time(-1, 11);
assertThat(checkNightMode(true)).isTrue();
}
@Test
public void test_dark_mode_after_custom_time() {
test_step_for_custom_time(-11, -1);
assertThat(checkNightMode(false)).isTrue();
}
@Test
public void test_dark_mode_before_custom_time() {
test_step_for_custom_time(2, 20);
assertThat(checkNightMode(false)).isTrue();
}
/**
* Sets custom time for night mode.
*
* @param fragment The DarkModeSettingsFragment.
* @param dialogId Dialog id for start time or end time.
* @param time The time to be set.
*/
private void setCustomTime(DarkModeSettingsFragment fragment, int dialogId, LocalTime time) {
Log.d(TAG, "Start to set custom time " + (dialogId == DIALOG_START_TIME ? "StartTime"
: "EndTime") + " to " + time.getHour() + ":" + time.getMinute());
TimePickerDialog startTimeDialog = (TimePickerDialog) fragment.onCreateDialog(dialogId);
startTimeDialog.updateTime(time.getHour(), time.getMinute());
startTimeDialog.onClick(startTimeDialog, BUTTON_POSITIVE);
}
private boolean checkNightMode(boolean isNightMode) {
int mask = (isNightMode ? Configuration.UI_MODE_NIGHT_YES : Configuration.UI_MODE_NIGHT_NO);
int mode = mInstrumentation.getTargetContext().getResources().getConfiguration().uiMode;
return (mode & mask) != 0;
}
@After
public void tearDown() {
Log.d(TAG, "tearDown.");
if (mUiModeManager.getNightMode() != UiModeManager.MODE_NIGHT_NO) {
mUiModeManager.setNightMode(UiModeManager.MODE_NIGHT_NO);
}
}
}

View File

@@ -0,0 +1,118 @@
/*
* Copyright (C) 2020 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.settings.fuelgauge.batterysaver;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assert_;
import android.app.Activity;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
import android.os.PowerManager;
import android.provider.Settings;
import android.util.Log;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.test.core.app.ActivityScenario;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.settings.Settings.BatterySaverSettingsActivity;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.testutils.AdbUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class BatterySaverButtonPreferenceControllerComponentTest {
private static final String TAG =
BatterySaverButtonPreferenceControllerComponentTest.class.getSimpleName();
private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
private final PowerManager mManager =
(PowerManager) mInstrumentation.getTargetContext().getSystemService(
Context.POWER_SERVICE);
@Rule
public ActivityScenarioRule<BatterySaverSettingsActivity> rule = new ActivityScenarioRule<>(
new Intent(
Settings.ACTION_BATTERY_SAVER_SETTINGS).setFlags(
Intent.FLAG_ACTIVITY_NEW_TASK));
@Before
public void setUp() throws Exception {
mInstrumentation.getUiAutomation().executeShellCommand("dumpsys battery unplug");
mInstrumentation.getUiAutomation().executeShellCommand("settings get global low_power 0");
}
private BatterySaverButtonPreferenceController get_battery_saver_controller(Activity activity) {
BatterySaverButtonPreferenceController controller =
new BatterySaverButtonPreferenceController(
ApplicationProvider.getApplicationContext(), "battery_saver");
Fragment f =
((FragmentActivity) activity).getSupportFragmentManager().getFragments().get(0);
controller.displayPreference(((SettingsPreferenceFragment) f).getPreferenceScreen());
return controller;
}
@Test
public void test_check_battery_saver_button() throws Exception {
ActivityScenario scenario = rule.getScenario();
scenario.onActivity(activity -> {
BatterySaverButtonPreferenceController controller =
get_battery_saver_controller(activity);
controller.setChecked(true);
checkPowerSaverMode(true);
controller.setChecked(false);
checkPowerSaverMode(false);
});
}
@After
public void tearDown() {
mInstrumentation.getUiAutomation().executeShellCommand("settings get global low_power 0");
mInstrumentation.getUiAutomation().executeShellCommand("dumpsys battery reset");
}
private void checkPowerSaverMode(boolean enabled) {
//Check through adb. Note that this needs to be done first, or a wait and poll needs to be
//done to the manager.isPowerSaveMode(), because calling isPowerSaveMode immediately after
//setting it does not return true. It takes a while for isPowerSaveMode() to return the
//up-to-date value.
try {
assertThat(
AdbUtils.checkStringInAdbCommandOutput(TAG, "settings get global low_power",
null, enabled ? "1" : "0", 1000)).isTrue();
} catch (Exception e) {
Log.e(TAG, e.getMessage());
assert_().fail();
}
//Check through manager
assertThat(mManager.isPowerSaveMode() == enabled).isTrue();
}
}

View File

@@ -0,0 +1,137 @@
/*
* Copyright (C) 2021 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.settings.homepage;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assert_;
import android.app.Instrumentation;
import android.content.ComponentName;
import android.content.Intent;
import android.provider.Settings;
import android.util.Log;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import com.google.common.collect.ImmutableList;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.List;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class HomepageComponentTest {
public final String TAG = this.getClass().getSimpleName();
private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
@Test
public void test_launch_all_settings_in_home()
throws ClassNotFoundException {
List<Intent> launchIntents = ImmutableList.of(
// Wifi
// Implemented in WifiSettings2ActivityTest
// Connected devices
new Intent(Settings.ACTION_BLUETOOTH_SETTINGS),
// Applications
new Intent(Settings.ACTION_AUTO_ROTATE_SETTINGS),
// Notifications
new Intent(Settings.ACTION_NOTIFICATION_SETTINGS),
// Display
new Intent(Settings.ACTION_DISPLAY_SETTINGS),
// Battery
// Implemented in fuelgauge.batterysaver
// Storage
new Intent(Settings.ACTION_INTERNAL_STORAGE_SETTINGS),
// Sound
new Intent(Settings.ACTION_SOUND_SETTINGS),
// Display
new Intent(Settings.ACTION_DISPLAY_SETTINGS),
// Wallpaper
new Intent(mInstrumentation.getTargetContext(), Class.forName(
"com.android.settings.wallpaper.WallpaperSuggestionActivity")),
// A11y
new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS),
// Security
new Intent(Settings.ACTION_SECURITY_SETTINGS),
// Privacy
new Intent(Settings.ACTION_PRIVACY_SETTINGS),
// Location
new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS),
// Emergency ? EmergencyDashboardFragment
// TODO: find out launch method
// Password & Account
new Intent(Settings.ACTION_SYNC_SETTINGS),
// Digital wellbeing
// Use IA link
new Intent().setComponent(
new ComponentName(
"com.google.android.apps.wellbeing",
"com.google.android.apps.wellbeing.settings"
+ ".TopLevelSettingsActivity")),
// Google
// Use IA link
new Intent().setComponent(
new ComponentName(
"com.google.android.gms",
"com.google.android.gms.app.settings.GoogleSettingsIALink")),
// System ?
// TODO: find out launch method.
// About
new Intent(Settings.ACTION_DEVICE_INFO_SETTINGS)
);
for (Intent intent : launchIntents) {
Log.d(TAG, "Start to launch intent " + intent.getAction());
try {
mInstrumentation.getTargetContext()
.startActivity(intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
} catch (Exception e) {
Log.e(TAG, "Launch with exception. " + e.toString());
assert_().fail();
}
// Launch success without exception.
assertThat(Boolean.TRUE).isTrue();
}
}
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright (C) 2021 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.settings.network;
import static com.google.common.truth.Truth.assertThat;
import android.app.Instrumentation;
import android.provider.Settings;
import android.provider.SettingsSlicesContract;
import android.util.Log;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.settings.testutils.UiUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class AirplaneModePreferenceControllerComponentTest {
// Airplane on off status
private static final int ON = 1;
private static final int OFF = 0;
public final String TAG = this.getClass().getName();
private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
private boolean mOriginAirplaneModeIsOn;
@Before
public void setUp() {
// Make sure origin airplane mode is OFF.
mOriginAirplaneModeIsOn = is_airplane_mode_on();
if (mOriginAirplaneModeIsOn) {
Log.d(TAG, "Origin airplane mode is on, turn it off.");
Settings.Global.putInt(mInstrumentation.getTargetContext().getContentResolver(),
Settings.Global.AIRPLANE_MODE_ON, OFF);
}
}
/**
* Tests on/off airplane mode repeatedly.
* Previously, a bug describe that crash issue if user on off airplane mode repeatedly.
* This case try to switch on & off airplane mode for 10 times to check crash issue.
*/
@Test
public void test_on_off_airplane_mode_multiple_times() {
AirplaneModePreferenceController controller =
new AirplaneModePreferenceController(mInstrumentation.getTargetContext(),
SettingsSlicesContract.KEY_AIRPLANE_MODE);
for (int i = 0; i < 10; ++i) {
Log.d(TAG, "Test #" + (i + 1));
controller.setChecked(true);
assertThat(UiUtils.waitUntilCondition(1000,
() -> is_airplane_mode_on())).isTrue();
controller.setChecked(false);
assertThat(UiUtils.waitUntilCondition(1000,
() -> !is_airplane_mode_on())).isTrue();
}
}
private boolean is_airplane_mode_on() {
return Settings.System.getInt(
mInstrumentation.getTargetContext().getContentResolver(),
Settings.Global.AIRPLANE_MODE_ON, OFF) != 0;
}
@After
public void tearDown() {
if (is_airplane_mode_on() != mOriginAirplaneModeIsOn) {
Settings.Global.putInt(mInstrumentation.getTargetContext().getContentResolver(),
Settings.Global.AIRPLANE_MODE_ON, (
mOriginAirplaneModeIsOn ? ON : OFF));
}
}
}

View File

@@ -0,0 +1,175 @@
/*
* Copyright (C) 2021 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.settings.network.telephony;
import static com.android.settings.testutils.CommonUtils.set_wifi_enabled;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
import android.net.wifi.WifiManager;
import android.telecom.TelecomManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.Log;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.LifecycleOwner;
import androidx.test.core.app.ActivityScenario;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.settings.Settings;
import com.android.settings.testutils.CommonUtils;
import com.android.settings.testutils.UiUtils;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.mobile.dataservice.MobileNetworkInfoEntity;
import com.android.settingslib.mobile.dataservice.SubscriptionInfoEntity;
import org.junit.After;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import java.io.IOException;
import java.net.URL;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class MobileDataPreferenceControllerComponentTest {
@Mock
private Lifecycle mLifecycle;
@Mock
private LifecycleOwner mLifecycleOwner;
public static final int TIMEOUT = 2000;
private static int sSubscriptionId = 2;
public final String TAG = this.getClass().getName();
private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
private final WifiManager mWifiManager =
(WifiManager) mInstrumentation.getTargetContext().getSystemService(
Context.WIFI_SERVICE);
private final TelephonyManager mTelephonyManager =
(TelephonyManager) mInstrumentation.getTargetContext().getSystemService(
Context.TELEPHONY_SERVICE);
private final TelecomManager mTelecomManager =
(TelecomManager) mInstrumentation.getTargetContext().getSystemService(
Context.TELECOM_SERVICE);
@Rule
public ActivityScenarioRule<Settings.MobileNetworkActivity>
rule = new ActivityScenarioRule<>(
new Intent(android.provider.Settings.ACTION_DATA_ROAMING_SETTINGS)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
private boolean mOriginDataEnabled;
private boolean mOriginWifiEnabled;
@Before
public void setUp() {
mOriginWifiEnabled = mWifiManager.isWifiEnabled();
// Disable wifi
set_wifi_enabled(false);
// Enable mobile data
mOriginDataEnabled = mTelephonyManager.isDataEnabled();
if (!mOriginDataEnabled) {
mTelephonyManager.enableDataConnectivity();
}
// Current sim card is not available for data network.
sSubscriptionId = SubscriptionManager.getDefaultDataSubscriptionId();
Assume.assumeTrue("Device cannot mobile network! Should ignore test.",
sSubscriptionId != SubscriptionManager.INVALID_SUBSCRIPTION_ID);
int simState = mTelephonyManager.getSimState();
Assume.assumeTrue("Sim card is not ready. Expect: " + TelephonyManager.SIM_STATE_READY
+ ", Actual: " + simState, simState == TelephonyManager.SIM_STATE_READY);
}
/**
* Tests the mobile network is disabled.
* Precondition:
* Disabled wifi, and enabled mobile network.
* Steps:
* 1. Launch mobile data page.
* 2. Turn off mobile data from switch.
* [Check]
* - Mobile data is turned off via TelephonyManager.
* - Open socket connection https://www.google.net and check the connection failed.
*/
@Test
public void test_disable_mobile_network() {
ActivityScenario scenario = rule.getScenario();
scenario.onActivity(activity -> {
try {
URL url = new URL("https://www.google.net");
MobileDataPreferenceController controller = new MobileDataPreferenceController(
mInstrumentation.getTargetContext(), "mobile_data", mLifecycle,
mLifecycleOwner, sSubscriptionId);
FragmentManager manager = ((FragmentActivity) activity).getSupportFragmentManager();
controller.init(manager, sSubscriptionId, mock(SubscriptionInfoEntity.class), mock(
MobileNetworkInfoEntity.class));
// Make sure mobile network can connect at first.
assertThat(UiUtils.waitUntilCondition(1000,
() -> CommonUtils.connectToURL(url))).isTrue();
Log.d(TAG, "Start to click ");
controller.setChecked(false);
Log.d(TAG, "Set Checked, wait for fully close.");
// Assert the configuration is set.
assertThat(UiUtils.waitUntilCondition(10000,
() -> !mTelephonyManager.isDataEnabled())).isTrue();
// Assert the network is not connectable.
assertThat(UiUtils.waitUntilCondition(1000,
() -> CommonUtils.connectToURL(url))).isFalse();
} catch (IOException e) {
}
});
}
@After
public void tearDown() {
// Restore wifi status wifi
set_wifi_enabled(mOriginWifiEnabled);
// Restore mobile data status
if (mOriginDataEnabled != mTelephonyManager.isDataEnabled()) {
if (mOriginDataEnabled) {
mTelephonyManager.enableDataConnectivity();
} else {
mTelephonyManager.disableDataConnectivity();
}
}
}
}

View File

@@ -0,0 +1,126 @@
/*
* Copyright (C) 2021 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.settings.notification;
import static com.google.common.truth.Truth.assertThat;
import android.app.Instrumentation;
import android.content.Intent;
import android.provider.Settings;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.test.core.app.ActivityScenario;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.lifecycle.Stage;
import com.android.settings.testutils.CommonUtils;
import com.android.settings.testutils.UiUtils;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Arrays;
import java.util.List;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class AppNotificationComponentTest {
private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
private final String mNoSlientAppName = "com.google.android.dialer";
public final String TAG = this.getClass().getName();
@Rule
public ActivityScenarioRule<com.android.settings.Settings.AppNotificationSettingsActivity>
rule = new ActivityScenarioRule<>(
new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
.putExtra(Settings.EXTRA_APP_PACKAGE, mNoSlientAppName)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
/**
* Tests user should not able to modify notification settings for some system apps.
* In this case, test `phone` app that will disabled notification configuration.
* Steps:
* 1. Open notification page of phone app.
* 2. Checks system privilege notification should not able to be changed.
*/
@Test
public void test_special_app_could_not_disable_notification() {
List<String> disabledList = Arrays.asList("Default", "Incoming calls",
"Background Processing", "Missed calls",
"Ongoing calls", "Voicemails");
ActivityScenario ac = rule.getScenario();
ac.onActivity(
activity -> {
View recyclerView = activity.findViewById(
CommonUtils.getResId("recycler_view"));
if (recyclerView == null) {
Log.d("UI_UTILS",
"Target not found: R.id.recycler_view #" + Integer.toHexString(
CommonUtils.getResId("recycler_view")));
UiUtils.dumpView(UiUtils.getFirstViewFromActivity(activity));
assertThat(Boolean.TRUE).isFalse();
}
UiUtils.waitUntilCondition(5000,
() -> recyclerView.findViewById(CommonUtils.getResId("recycler_view"))
!= null);
View mainSwitchBar = recyclerView.findViewById(
CommonUtils.getResId("main_switch_bar"));
assertThat(mainSwitchBar.isEnabled()).isEqualTo(false);
Log.d(TAG, "main switch bar = " + mainSwitchBar.isEnabled());
UiUtils.waitForActivitiesInStage(10000, Stage.RESUMED);
Log.d(TAG, "In stage!.");
UiUtils.dumpView(UiUtils.getFirstViewFromActivity(activity));
// The privileges are under the recycle view. Fetch all of them and check.
ViewGroup viewGroup = (ViewGroup) recyclerView;
for (int i = 0; i < viewGroup.getChildCount(); i++) {
if (viewGroup.getChildAt(i) instanceof LinearLayout) {
// A notification in Settings should have both switch_widget and text.
// There has another circle pin is no belongs to Settings package.
// But belongs to Switch in Android.
View sWidget = viewGroup.getChildAt(i).findViewById(
CommonUtils.getResId("switchWidget"));
TextView sText = viewGroup.getChildAt(i).findViewById(
android.R.id.title);
if (sText != null && sWidget != null
&& disabledList.stream().anyMatch(
str -> str.equals(sText.getText().toString().trim()))) {
assertThat(sWidget.isEnabled()).isFalse();
}
}
}
}
);
}
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright (C) 2020 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.settings.privacy;
import static com.google.common.truth.Truth.assertThat;
import android.app.Instrumentation;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.settings.testutils.AdbUtils;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class EnabledContentCapturePreferenceControllerComponentTest {
private Instrumentation mInstrumentation;
private static final String TAG =
EnabledContentCapturePreferenceControllerComponentTest.class.getSimpleName();
@Before
public void setUp() {
if (null == mInstrumentation) {
mInstrumentation = InstrumentationRegistry.getInstrumentation();
}
}
@Test
public void test_uncheck_content_capture() throws Exception {
content_capture_checkbox_test_helper(false);
}
@Test
public void test_check_content_capture() throws Exception {
content_capture_checkbox_test_helper(true);
}
private void content_capture_checkbox_test_helper(boolean check) throws Exception {
EnableContentCapturePreferenceController enableContentCapturePreferenceController =
new EnableContentCapturePreferenceController(
ApplicationProvider.getApplicationContext(),
"Test_key");
enableContentCapturePreferenceController.setChecked(check);
//Check through adb command
assertThat(AdbUtils.checkStringInAdbCommandOutput(TAG, "dumpsys content_capture",
"Users disabled by Settings: ", check ? "{}" : "{0=true}", 1000)).isTrue();
}
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright (C) 2020 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.settings.testutils;
import android.os.ParcelFileDescriptor;
import androidx.test.platform.app.InstrumentationRegistry;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.stream.Collectors;
public class AdbUtils {
public static String getCallerClassName() {
StackTraceElement[] stElements = Thread.currentThread().getStackTrace();
for (int i = 1; i < stElements.length; i++) {
StackTraceElement ste = stElements[i];
if (!ste.getClassName().equals(new Object() {
}.getClass().getEnclosingClass().getName()) && ste.getClassName().indexOf(
"java.lang.Thread") != 0) {
return ste.getClassName();
}
}
return null;
}
public static boolean checkStringInAdbCommandOutput(String logTag, String command,
String prefix, String target, int timeoutInMillis) throws Exception {
long start = System.nanoTime();
//Sometimes the change do no reflect in adn output immediately, so need a wait and poll here
while (System.nanoTime() - start < (timeoutInMillis * 1000000)) {
String result = shell(command);
if (result.contains(prefix == null ? "" : prefix)
&& result.contains(target == null ? "" : target)) {
return true;
} else {
Thread.sleep(100);
}
}
return false;
}
public static String shell(String shellCommand) {
String returnValue = "";
try (ParcelFileDescriptor.AutoCloseInputStream in =
new ParcelFileDescriptor.AutoCloseInputStream(
InstrumentationRegistry.getInstrumentation()
.getUiAutomation()
.executeShellCommand(shellCommand))) {
try (BufferedReader br =
new BufferedReader(
new InputStreamReader(in, StandardCharsets.UTF_8))) {
returnValue = br.lines().collect(Collectors.joining());
}
} catch (IOException e) {
e.printStackTrace();
}
return returnValue;
}
}

View File

@@ -0,0 +1,165 @@
/*
* Copyright (C) 2021 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.settings.testutils;
import android.app.Activity;
import android.app.Instrumentation;
import android.content.Context;
import android.graphics.Bitmap;
import android.net.wifi.WifiManager;
import android.os.Environment;
import android.os.PowerManager;
import android.os.StrictMode;
import android.os.SystemClock;
import android.util.Log;
import android.view.View;
import androidx.test.platform.app.InstrumentationRegistry;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import javax.net.ssl.HttpsURLConnection;
public class CommonUtils {
private static final String TAG = CommonUtils.class.getSimpleName();
private static final Instrumentation sInstrumentation =
InstrumentationRegistry.getInstrumentation();
private static final WifiManager sWifiManager =
(WifiManager) sInstrumentation.getTargetContext().getSystemService(
Context.WIFI_SERVICE);
private static final PowerManager sPowerManager =
(PowerManager) sInstrumentation.getTargetContext().getSystemService(
Context.POWER_SERVICE);
public static void takeScreenshot(Activity activity) {
long now = System.currentTimeMillis();
try {
// image naming and path to include sd card appending name you choose for file
String mPath =
Environment.getExternalStorageDirectory().toString() + "/" + now + ".jpg";
Log.d(TAG, "screenshot path is " + mPath);
// create bitmap screen capture
View v1 = activity.getWindow().getDecorView().getRootView();
v1.setDrawingCacheEnabled(true);
Bitmap bitmap = Bitmap.createBitmap(v1.getDrawingCache());
v1.setDrawingCacheEnabled(false);
File imageFile = new File(mPath);
FileOutputStream outputStream = new FileOutputStream(imageFile);
int quality = 100;
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, outputStream);
outputStream.flush();
outputStream.close();
} catch (Throwable e) {
// Several error may come out with file handling or DOM
e.printStackTrace();
}
}
public static boolean connectToURL(URL url) {
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
HttpURLConnection connection = null;
try {
connection = (HttpsURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(8000);
connection.setReadTimeout(8000);
connection.connect();
if (HttpURLConnection.HTTP_OK == connection.getResponseCode()) {
InputStream in = connection.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
StringBuilder response = new StringBuilder();
String line;
while (null != (line = reader.readLine())) {
response.append(line);
}
Log.d(TAG, "Connection success! " + response.toString());
return true;
}
} catch (Exception e) {
Log.e(TAG, e.toString());
e.printStackTrace();
return false;
} finally {
if (null != connection) {
connection.disconnect();
}
}
Log.d(TAG, "End, return false.");
return false;
}
/**
* Return a resource identifier for the given resource name in Settings app.
*
* @param name The name of the desired resource.
* @return int The associated resource identifier. Returns 0 if no such resource was found. (0
* is not a valid resource ID.)
*/
public static int getResId(String name) {
return InstrumentationRegistry.getInstrumentation().getTargetContext().getResources()
.getIdentifier(name, "id", Constants.SETTINGS_PACKAGE_NAME);
}
public static void reopenScreen() {
sPowerManager.goToSleep(SystemClock.uptimeMillis());
// According to local test, we need to sleep to wait it fully processed.
// 1000 ms is a good value to sleep, otherwise it might cause keyDispatchingTimedOut.
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
UiUtils.waitUntilCondition(1000, () -> !sPowerManager.isInteractive());
sPowerManager.wakeUp(SystemClock.uptimeMillis());
UiUtils.waitUntilCondition(1000, () -> sPowerManager.isInteractive());
// After power on screen, need to unlock and goto home page.
sPowerManager.wakeUp(1000, PowerManager.WAKE_REASON_POWER_BUTTON, "Wakeup");
}
/**
* Sets wifi status to given enable / disable via ADB command.
*/
public static void set_wifi_enabled(boolean enable) {
final int timeoutMsec = 10000;
Log.d(TAG, "Set wifi status to " + enable);
if (sWifiManager.isWifiEnabled() != enable) {
AdbUtils.shell("svc wifi " + (enable ? "enable" : "disable"));
if (!UiUtils.waitUntilCondition(timeoutMsec,
() -> sWifiManager.isWifiEnabled() == enable)) {
Log.e(TAG, "Cannot set wifi to " + (enable ? "enabl" : "disable") + ", timeout "
+ timeoutMsec + " (ms).");
Log.e(TAG, "See logcat for more information.");
}
Log.d(TAG, "After configuration wifi status = " + sWifiManager.isWifiEnabled());
} else {
Log.d(TAG, "Wifi is enable is already " + enable + ", no need to change.");
}
}
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright (C) 2021 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.settings.testutils;
public class Constants {
public static final long ACTIVITY_LAUNCH_WAIT_TIMEOUT = 5000;
public static final long VIEW_APPEAR_WAIT_MEDIUM_TIMEOUT = 5000;
public static final long WIFI_CONNECT_WAIT_TIMEOUT = 15000;
public static final String SETTINGS_PACKAGE_NAME = "com.android.settings";
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright (C) 2020 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.settings.testutils;
import android.app.Activity;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import androidx.fragment.app.FragmentActivity;
import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry;
import androidx.test.runner.lifecycle.Stage;
import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Supplier;
public class UiUtils {
private static final String TAG = "UI_UTILS";
public static boolean waitUntilCondition(long timeoutInMillis, Supplier<Boolean> condition) {
long start = System.nanoTime();
while (System.nanoTime() - start < (timeoutInMillis * 1000000)) {
try {
//Eat NPE from condition because there's a concurrency issue that when calling
//findViewById when the view hierarchy is still rendering, it sometimes encounter
//null views that may exist few milliseconds before, and causes a NPE.
if (condition.get()) {
return true;
}
} catch (NullPointerException e) {
e.printStackTrace();
}
}
Log.w(TAG, "Condition not match and timeout for waiting " + timeoutInMillis + "(ms).");
return false;
}
public static boolean waitForActivitiesInStage(long timeoutInMillis, Stage stage) {
final Collection<Activity> activities = new ArrayList<>();
waitUntilCondition(Constants.ACTIVITY_LAUNCH_WAIT_TIMEOUT, () -> {
activities.addAll(
ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(
Stage.RESUMED));
return activities.size() > 0;
});
return activities.size() > 0;
}
public static void dumpView(View view) {
dumpViewRecursive(view, 0, 0, 0);
}
public static View getFirstViewFromActivity(Activity activity) {
return ((FragmentActivity) activity).getSupportFragmentManager().getFragments().get(
0).getView();
}
private static void dumpViewRecursive(View view, int layer, int index, int total) {
if (view instanceof ViewGroup) {
Log.i(TAG, "L[" + layer + "] PARENT -> " + (index + 1) + "/" + total + " >> "
+ view.toString());
System.out.println(
TAG + " L[" + layer + "] PARENT -> " + (index + 1) + "/" + total + " >> "
+ view.toString());
for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
dumpViewRecursive(((ViewGroup) view).getChildAt(i), layer + 1, i + 1,
((ViewGroup) view).getChildCount());
}
} else {
Log.i(TAG, "L[" + layer + "] =END= -> " + (index + 1) + "/" + total + " >> "
+ view.toString());
}
}
}

View File

@@ -0,0 +1,139 @@
/*
* Copyright (C) 2021 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.settings.users;
import static com.google.common.truth.Truth.assertThat;
import android.app.Instrumentation;
import android.content.Intent;
import android.content.pm.UserInfo;
import android.os.UserManager;
import android.util.Log;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.test.core.app.ActivityScenario;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.settings.Settings;
import com.android.settings.testutils.AdbUtils;
import com.android.settings.testutils.UiUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;
import java.util.stream.Collectors;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class UserSettingsComponentTest {
public static final int TIMEOUT = 2000;
private static final int USER_TYPE_RESTRICTED_PROFILE = 2;
public final String TAG = this.getClass().getName();
private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
private final ArrayList<Integer> mOriginUserIds = new ArrayList<>();
private final UserManager mUserManager =
(UserManager) mInstrumentation.getTargetContext().getSystemService("user");
@Rule
public ActivityScenarioRule<Settings.UserSettingsActivity>
rule = new ActivityScenarioRule<>(
new Intent(android.provider.Settings.ACTION_USER_SETTINGS)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
@Before
public void setUp() {
for (UserInfo info : mUserManager.getUsers()) {
mOriginUserIds.add(info.id);
}
// Enable multiple user switch.
if (!mUserManager.isUserSwitcherEnabled()) {
android.provider.Settings.Global.putInt(
mInstrumentation.getTargetContext().getContentResolver(),
android.provider.Settings.Global.USER_SWITCHER_ENABLED, 1);
}
}
@Test
public void test_new_user_on_multiple_setting_page() throws IOException {
String randomUserName = gendrate_random_name(10);
ActivityScenario scenario = rule.getScenario();
scenario.onActivity(activity -> {
Fragment f =
((FragmentActivity) activity).getSupportFragmentManager().getFragments().get(0);
UserSettings us = (UserSettings) f;
Log.d(TAG, "Start to add user :" + randomUserName);
us.createUser(USER_TYPE_RESTRICTED_PROFILE, randomUserName);
});
assertThat(
UiUtils.waitUntilCondition(5000, () -> mUserManager.getAliveUsers().stream().filter(
(user) -> user.name.equals(
randomUserName)).findFirst().isPresent())).isTrue();
}
@After
public void tearDown() {
int retryNumber = 5;
for (int i = 0; i < retryNumber; ++i) {
int currentUsersCount = mUserManager.getUserCount();
if (currentUsersCount == mOriginUserIds.size()) {
break;
} else if (i != 0) {
Log.d(TAG, "[tearDown] User not fully removed. Retry #" + (i = 1) + " of total "
+ mOriginUserIds.size());
}
for (UserInfo info : mUserManager.getUsers()) {
if (mOriginUserIds.contains(info.id)) {
continue;
}
Log.d(TAG, "[tearDown] Clean up user {" + info.id + "}:" + info.name);
try {
AdbUtils.shell("pm remove-user " + info.id);
} catch (Exception e) {
Log.w(TAG, "[tearDown] Error occurs while removing user. " + e.toString());
}
}
}
}
private String gendrate_random_name(int length) {
String seed = "abcdefghijklmnopqrstuvwxyABCDEFGHIJKLMNOPQSTUVWXYZ";
Random r1 = new Random();
String result = "";
for (int i = 0; i < length; ++i) {
result = result + seed.charAt(r1.nextInt(seed.length() - 1));
}
if (mUserManager.getAliveUsers().stream().map(user -> user.name).collect(
Collectors.toList()).contains(result)) {
Log.d(TAG, "Name repeated! add padding 'rpt' in the end of name.");
result += "rpt";
}
return result;
}
}

View File

@@ -0,0 +1,126 @@
/*
* Copyright (C) 2021 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.settings.wifi;
import static com.google.common.truth.Truth.assertThat;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.provider.Settings;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.MediumTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry;
import androidx.test.runner.lifecycle.Stage;
import com.android.settings.R;
import com.android.settings.Settings.NetworkProviderSettingsActivity;
import com.android.settings.fuelgauge.batterysaver.BatterySaverButtonPreferenceControllerComponentTest;
import com.android.settings.network.NetworkProviderSettings;
import com.android.settings.testutils.CommonUtils;
import com.android.settings.testutils.Constants;
import com.android.settings.testutils.UiUtils;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.net.URL;
import java.util.List;
@RunWith(AndroidJUnit4.class)
@MediumTest
/*
This test is just for demonstration purpose. For component tests, this approach is not recommended.
The reason why it is written this way is because the current Settings app wifi codes have tight
coupling with UI, so it's not easy to drive from API without binding the test deeply with the code.
*/
public class WifiSettings2ActivityTest {
private static final String TAG =
BatterySaverButtonPreferenceControllerComponentTest.class.getSimpleName();
private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
@Test @Ignore
public void test_connect_to_wifi() throws Exception {
//For some reason the ActivityScenario gets null activity here
mInstrumentation.getTargetContext().startActivity(
new Intent(Settings.ACTION_WIFI_SETTINGS).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
UiUtils.waitForActivitiesInStage(Constants.ACTIVITY_LAUNCH_WAIT_TIMEOUT, Stage.RESUMED);
final NetworkProviderSettings[] settings = new NetworkProviderSettings[1];
mInstrumentation.runOnMainSync(() -> {
NetworkProviderSettingsActivity activity = (NetworkProviderSettingsActivity)
ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(
Stage.RESUMED).iterator().next();
settings[0] =
(NetworkProviderSettings) activity.getSupportFragmentManager().getFragments()
.get(0);
});
//For some reason this view does not appear immediately after the fragment is resumed.
View root = settings[0].getView();
UiUtils.waitUntilCondition(Constants.VIEW_APPEAR_WAIT_MEDIUM_TIMEOUT,
() -> root.findViewById(R.id.settings_button) != null);
View view = root.findViewById(R.id.settings_button);
view.callOnClick();
UiUtils.waitForActivitiesInStage(Constants.ACTIVITY_LAUNCH_WAIT_TIMEOUT, Stage.RESUMED);
Button[] button = new Button[1];
mInstrumentation.runOnMainSync(() -> {
FragmentActivity activity =
(FragmentActivity) ActivityLifecycleMonitorRegistry.getInstance()
.getActivitiesInStage(Stage.RESUMED).iterator().next();
List<Fragment> fragments = activity.getSupportFragmentManager().getFragments();
Log.d(TAG, "fragment class is " + fragments.get(0).getClass());
button[0] = fragments.get(0).getView().findViewById(R.id.button3);
});
//HttpURLConnection needs to run outside of main thread, so running it in the test thread
final URL url = new URL("https://www.google.net/");
//Make sure the connectivity is available before disconnecting from wifi
assertThat(CommonUtils.connectToURL(url)).isTrue();
//Disconnect from wifi
button[0].callOnClick();
//Make sure the Internet connectivity is gone
assertThat(CommonUtils.connectToURL(url)).isFalse();
//Connect to wifi
button[0].callOnClick();
ConnectivityManager manager =
(ConnectivityManager) mInstrumentation.getTargetContext().getSystemService(
Context.CONNECTIVITY_SERVICE);
//For some reason I can't find a way to tell the time that the internet connectivity is
//actually available with the new, non-deprecated ways, so I still need to use this.
UiUtils.waitUntilCondition(Constants.WIFI_CONNECT_WAIT_TIMEOUT,
() -> manager.getActiveNetworkInfo().isConnected());
//Make sure the connectivity is back again
assertThat(CommonUtils.connectToURL(url)).isTrue();
}
}

View File

@@ -0,0 +1,79 @@
/*
* 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 com.android.settings.wifi.dpp;
import static android.app.Activity.RESULT_OK;
import static androidx.test.espresso.intent.Intents.intended;
import static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction;
import static com.google.common.truth.Truth.assertThat;
import static org.hamcrest.Matchers.equalTo;
import android.app.KeyguardManager;
import androidx.activity.result.ActivityResult;
import androidx.test.core.app.ActivityScenario;
import androidx.test.espresso.intent.Intents;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class WifiDppConfiguratorAuthActivityTest {
@Before
public void setup() {
Intents.init();
}
@After
public void teardown() throws Exception {
Intents.release();
}
@Test
public void launchActivity_sendAuthIntent() {
ActivityScenario<WifiDppConfiguratorAuthActivity> activityScenario =
ActivityScenario.launch(WifiDppConfiguratorAuthActivity.class);
assertThat(activityScenario).isNotNull();
intended(hasAction(equalTo(KeyguardManager.ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER)));
}
@Test
public void launchActivity_sendQrCodeIntent() {
ActivityScenario.launch(WifiDppConfiguratorAuthActivity.class).onActivity(activity ->
activity.onAuthResult(new ActivityResult(RESULT_OK, /* data= */ null))
);
intended(hasAction(
equalTo(WifiDppConfiguratorActivity.ACTION_CONFIGURATOR_QR_CODE_GENERATOR)));
}
@Test
public void launchActivity_shouldFinish() {
ActivityScenario.launch(WifiDppConfiguratorAuthActivity.class).onActivity(activity -> {
activity.onAuthResult(new ActivityResult(RESULT_OK, /* data= */ null));
assertThat(activity.isFinishing()).isTrue();
});
}
}

View File

@@ -0,0 +1,100 @@
/*
* Copyright (C) 2021 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.settingslib.applications;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import androidx.test.core.app.ActivityScenario;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.settings.Settings;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.UUID;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class ApplicationStateComponentTest {
private static final String TAG =
ApplicationStateComponentTest.class.getSimpleName();
private Context mRuntimeApplication;
private ApplicationsState mApplicationsState;
@Rule
public ActivityScenarioRule<Settings.ManageApplicationsActivity> rule =
new ActivityScenarioRule<>(
new Intent(
android.provider.Settings.ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
private ApplicationsState.AppEntry createAppEntry(String label, String packageName, int id) {
ApplicationInfo appInfo = createApplicationInfo(packageName, id);
ApplicationsState.AppEntry appEntry = new ApplicationsState.AppEntry(mRuntimeApplication,
appInfo, id);
appEntry.label = label;
appEntry.mounted = true;
return appEntry;
}
private ApplicationInfo createApplicationInfo(String packageName, int uid) {
ApplicationInfo appInfo = new ApplicationInfo();
appInfo.sourceDir = "foo";
appInfo.flags |= ApplicationInfo.FLAG_INSTALLED;
appInfo.storageUuid = UUID.randomUUID();
appInfo.packageName = packageName;
appInfo.uid = uid;
return appInfo;
}
@Test
public void test_all_apps_sorting_alpha() {
// TODO: Potential unit test candidate.
// To test all app list has sorted alphabetical, only need to verify sort function.
// This case focus on logic in sort function, and ignore origin locale sorting rule by Java.
ActivityScenario scenario = rule.getScenario();
scenario.onActivity(activity -> {
mRuntimeApplication = activity.getApplication();
mApplicationsState = ApplicationsState.getInstance(activity.getApplication());
ApplicationsState.AppEntry entry1 = createAppEntry("Info01", "Package1", 0);
ApplicationsState.AppEntry entry2 = createAppEntry("Info02", "Package1", 0);
ApplicationsState.AppEntry entry3 = createAppEntry("Info01", "Package2", 0);
ApplicationsState.AppEntry entry4 = createAppEntry("Info02", "Package2", 0);
ApplicationsState.AppEntry entry5 = createAppEntry("Info02", "Package2", 1);
assertThat(ApplicationsState.ALPHA_COMPARATOR.compare(entry1, entry2)).isEqualTo(-1);
assertThat(ApplicationsState.ALPHA_COMPARATOR.compare(entry2, entry3)).isEqualTo(1);
assertThat(ApplicationsState.ALPHA_COMPARATOR.compare(entry3, entry2)).isEqualTo(-1);
assertThat(ApplicationsState.ALPHA_COMPARATOR.compare(entry3, entry3)).isEqualTo(0);
assertThat(ApplicationsState.ALPHA_COMPARATOR.compare(entry1, entry3)).isEqualTo(-1);
assertThat(ApplicationsState.ALPHA_COMPARATOR.compare(entry4, entry5)).isEqualTo(-1);
assertThat(ApplicationsState.ALPHA_COMPARATOR.compare(entry5, entry3)).isEqualTo(1);
});
}
}

View File

@@ -0,0 +1,29 @@
// Copyright (C) 2020 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_team: "trendy_team_android_settings_app",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "packages_apps_Settings_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["packages_apps_Settings_license"],
}
android_test {
name: "InstrumentedEntitlementApp",
srcs: ["src/**/*.java"],
test_suites: ["device-tests"],
}

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
* Copyright (C) 2020 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.test.entitlement">
<application>
<activity android:name=".InstrumentedEntitlementActivity"
android:label="InstrumentedEntitlementActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@@ -0,0 +1,22 @@
/*
* Copyright (C) 2020 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.test.entitlement;
import android.app.Activity;
// Instrumented activity for TetherProvisioningActivityTest
public class InstrumentedEntitlementActivity extends Activity {
}

View File

@@ -0,0 +1 @@
Directory for legacy settings unit tests, which are no longer maintained.

View File

@@ -0,0 +1,55 @@
/*
* Copyright (C) 2016 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.settings;
import static com.android.settings.UserCredentialsSettings.Credential;
import android.os.Parcel;
import android.os.Process;
import android.test.InstrumentationTestCase;
import androidx.test.filters.SmallTest;
/**
* User credentials settings fragment tests
*
* To run the test, use command:
* adb shell am instrument -e class com.android.settings.UserCredentialsTest
* -w com.android.settings.tests.unit/androidx.test.runner.AndroidJUnitRunner
*
*/
public class UserCredentialsTest extends InstrumentationTestCase {
private static final String TAG = "UserCredentialsTests";
@SmallTest
public void testCredentialIsParcelable() {
final String alias = "credential-test-alias";
Credential c = new Credential(alias, Process.SYSTEM_UID);
c.storedTypes.add(Credential.Type.CA_CERTIFICATE);
c.storedTypes.add(Credential.Type.USER_KEY);
Parcel p = Parcel.obtain();
c.writeToParcel(p, /* flags */ 0);
p.setDataPosition(0);
Credential r = Credential.CREATOR.createFromParcel(p);
assertEquals(c.alias, r.alias);
assertEquals(c.uid, r.uid);
assertEquals(c.storedTypes, r.storedTypes);
}
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright (C) 2016 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.settings;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.when;
import android.content.pm.UserInfo;
import android.os.UserHandle;
import android.os.UserManager;
import android.test.AndroidTestCase;
import androidx.test.filters.SmallTest;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.Arrays;
public class UtilsTest extends AndroidTestCase {
private static final int TEST_PRIMARY_USER_ID = 10;
private static final int TEST_MANAGED_PROFILE_ID = 11;
@Mock private UserManager mUserManager;
@Override
public void setUp() throws Exception {
super.setUp();
// // this is necessary for mockito to work
System.setProperty("dexmaker.dexcache", getContext().getCacheDir().toString());
MockitoAnnotations.initMocks(this);
when(mUserManager.getUserHandle()).thenReturn(TEST_PRIMARY_USER_ID);
UserInfo primaryUser = new UserInfo(TEST_PRIMARY_USER_ID, null,
UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_PRIMARY);
when(mUserManager.getUserInfo(TEST_PRIMARY_USER_ID)).thenReturn(primaryUser);
UserInfo managedProfile = new UserInfo(TEST_MANAGED_PROFILE_ID, null,
UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_MANAGED_PROFILE);
when(mUserManager.getUserInfo(eq(TEST_MANAGED_PROFILE_ID))).thenReturn(managedProfile);
}
@SmallTest
public void testGetManagedProfile() {
UserHandle[] userHandles = new UserHandle[] {
new UserHandle(TEST_PRIMARY_USER_ID),
new UserHandle(TEST_MANAGED_PROFILE_ID)
};
when(mUserManager.getUserProfiles())
.thenReturn(new ArrayList<UserHandle>(Arrays.asList(userHandles)));
assertEquals(TEST_MANAGED_PROFILE_ID,
Utils.getManagedProfile(mUserManager).getIdentifier());
}
@SmallTest
public void testGetManagedProfile_notPresent() {
UserHandle[] userHandles = new UserHandle[] {
new UserHandle(TEST_PRIMARY_USER_ID)
};
when(mUserManager.getUserProfiles())
.thenReturn(new ArrayList<UserHandle>(Arrays.asList(userHandles)));
assertNull(Utils.getManagedProfile(mUserManager));
}
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.accessibility;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import android.app.Instrumentation;
import android.os.Bundle;
import android.os.Looper;
import androidx.test.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
import com.android.settings.Settings.AccessibilitySettingsActivity;
import com.android.settings.core.InstrumentedPreferenceFragment;
import com.android.settings.core.SubSettingLauncher;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class ToggleFeaturePreferenceFragmentTest {
private static final String SUMMARY_TEXT = "Here's some summary text";
@Rule
public final ActivityTestRule<AccessibilitySettingsActivity> mActivityRule =
new ActivityTestRule<>(AccessibilitySettingsActivity.class, true);
private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
@BeforeClass
public static void oneTimeSetup() {
if (Looper.myLooper() == null) {
Looper.prepare();
}
}
@Before
public void setUp() {
mInstrumentation.runOnMainSync(() -> {
MyToggleFeaturePreferenceFragment fragment = new MyToggleFeaturePreferenceFragment();
Bundle args = new Bundle();
args.putString(AccessibilitySettings.EXTRA_SUMMARY, SUMMARY_TEXT);
fragment.setArguments(args);
new SubSettingLauncher(mActivityRule.getActivity())
.setDestination(MyToggleFeaturePreferenceFragment.class.getName())
.setArguments(args)
.setSourceMetricsCategory(
InstrumentedPreferenceFragment.METRICS_CATEGORY_UNKNOWN)
.launch();
});
}
@Test
public void testSummaryTestDisplayed() {
onView(withText(SUMMARY_TEXT)).check(matches(isDisplayed()));
}
public static class MyToggleFeaturePreferenceFragment extends ToggleFeaturePreferenceFragment {
@Override
protected void onPreferenceToggled(String preferenceKey, boolean enabled) {
}
@Override
public int getMetricsCategory() {
return 0;
}
@Override
int getUserShortcutTypes() {
return 0;
}
}
}

View File

@@ -0,0 +1,91 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.accounts;
import static com.google.common.truth.Truth.assertThat;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.content.Context;
import android.content.Intent;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject;
import android.support.test.uiautomator.UiObjectNotFoundException;
import android.support.test.uiautomator.UiScrollable;
import android.support.test.uiautomator.UiSelector;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class AccountsSettingsTest {
private static final String ACCOUNTS = "Accounts";
private static final String ACCOUNT_TYPE = "com.settingstest.account-prefs";
private static final String PREF_TITLE = "Test preference for external account";
private UiDevice mDevice;
private Context mContext;
private String mTargetPackage;
@Before
public void setUp() {
mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
mContext = InstrumentationRegistry.getTargetContext();
mTargetPackage = mContext.getPackageName();
}
@Test
public void testExternalAccountInfoExists() throws UiObjectNotFoundException {
// add a test account
final String testAccountName = "Test Account";
final Account account = new Account(testAccountName, ACCOUNT_TYPE);
final AccountManager accountManager = AccountManager.get(mContext);
final boolean accountAdded =
accountManager.addAccountExplicitly(account, null /* password */, null /* userdata */);
assertThat(accountAdded).isTrue();
// launch Accounts Settings and select the test account
launchAccountsSettings();
mDevice.findObject(new UiSelector().text(testAccountName)).click();
final UiObject testPreference = mDevice.findObject(new UiSelector().text(PREF_TITLE));
// remove test account
accountManager.removeAccountExplicitly(account);
assertThat(testPreference.exists()).isTrue();
}
private void launchAccountsSettings() throws UiObjectNotFoundException {
// launch settings
Intent settingsIntent = new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_LAUNCHER)
.setPackage(mTargetPackage)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(settingsIntent);
// selects Accounts
final UiScrollable settings = new UiScrollable(
new UiSelector().packageName(mTargetPackage).scrollable(true));
final String titleAccounts = ACCOUNTS;
settings.scrollTextIntoView(titleAccounts);
mDevice.findObject(new UiSelector().text(titleAccounts)).click();
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.accounts;
import android.accounts.AbstractAccountAuthenticator;
import android.accounts.Account;
import android.accounts.AccountAuthenticatorResponse;
import android.accounts.AccountManager;
import android.accounts.NetworkErrorException;
import android.content.Context;
import android.os.Bundle;
public class Authenticator extends AbstractAccountAuthenticator {
public Authenticator(Context context) {
super(context);
}
@Override
public Bundle editProperties(AccountAuthenticatorResponse r, String s) {
return null;
}
@Override
public Bundle addAccount(AccountAuthenticatorResponse r, String s, String s2, String[] strings,
Bundle bundle) throws NetworkErrorException {
final Bundle result = new Bundle();
result.putString(AccountManager.KEY_ACCOUNT_NAME, "Test Account");
result.putString(AccountManager.KEY_ACCOUNT_TYPE, s);
return result;
}
@Override
public Bundle confirmCredentials(AccountAuthenticatorResponse r, Account account, Bundle bundle)
throws NetworkErrorException {
return null;
}
@Override
public Bundle getAuthToken(AccountAuthenticatorResponse r, Account account, String s,
Bundle bundle) throws NetworkErrorException {
return null;
}
@Override
public String getAuthTokenLabel(String s) {
return s;
}
@Override
public Bundle updateCredentials(AccountAuthenticatorResponse r, Account account, String s,
Bundle bundle) throws NetworkErrorException {
return null;
}
@Override
public Bundle hasFeatures(AccountAuthenticatorResponse r, Account account, String[] strings)
throws NetworkErrorException {
return null;
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.accounts;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class TestAuthService extends Service {
private Authenticator mAuthenticator;
@Override
public void onCreate() {
mAuthenticator = new Authenticator(this);
}
@Override
public IBinder onBind(Intent intent) {
return mAuthenticator.getIBinder();
}
}

View File

@@ -0,0 +1,138 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications.manageapplications;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import android.content.pm.ApplicationInfo;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.settingslib.applications.AppUtils;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.ApplicationsState.AppFilter;
import com.android.settingslib.applications.ApplicationsState.CompoundFilter;
import com.android.settingslib.applications.instantapps.InstantAppDataProvider;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.lang.reflect.Field;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class ManageApplicationsUnitTest {
@Test
public void getCompositeFilter_filtersVolumeForAudio() {
AppFilter filter =
ManageApplications.getCompositeFilter(
ManageApplications.LIST_TYPE_STORAGE,
ManageApplications.STORAGE_TYPE_MUSIC,
"uuid");
final ApplicationInfo info = new ApplicationInfo();
info.volumeUuid = "uuid";
info.category = ApplicationInfo.CATEGORY_AUDIO;
final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class);
appEntry.info = info;
assertThat(filter.filterApp(appEntry)).isTrue();
}
@Test
public void getCompositeFilter_filtersVolumeForVideo() {
AppFilter filter =
ManageApplications.getCompositeFilter(
ManageApplications.LIST_TYPE_MOVIES,
ManageApplications.STORAGE_TYPE_DEFAULT,
"uuid");
final ApplicationInfo info = new ApplicationInfo();
info.volumeUuid = "uuid";
info.category = ApplicationInfo.CATEGORY_VIDEO;
final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class);
appEntry.info = info;
assertThat(filter.filterApp(appEntry)).isTrue();
}
@Test
public void getCompositeFilter_filtersVolumeForGames() {
ApplicationsState.AppFilter filter =
ManageApplications.getCompositeFilter(
ManageApplications.LIST_TYPE_GAMES,
ManageApplications.STORAGE_TYPE_DEFAULT,
"uuid");
final ApplicationInfo info = new ApplicationInfo();
info.volumeUuid = "uuid";
info.category = ApplicationInfo.CATEGORY_GAME;
final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class);
appEntry.info = info;
assertThat(filter.filterApp(appEntry)).isTrue();
}
@Test
public void getCompositeFilter_isEmptyNormally() {
ApplicationsState.AppFilter filter =
ManageApplications.getCompositeFilter(
ManageApplications.LIST_TYPE_MAIN,
ManageApplications.STORAGE_TYPE_DEFAULT,
"uuid");
assertThat(filter).isNull();
}
@Test
public void getCompositeFilter_worksWithInstantApps() throws Exception {
Field field = AppUtils.class.getDeclaredField("sInstantAppDataProvider");
field.setAccessible(true);
field.set(AppUtils.class, (InstantAppDataProvider) (i -> true));
AppFilter filter =
ManageApplications.getCompositeFilter(
ManageApplications.LIST_TYPE_STORAGE,
ManageApplications.STORAGE_TYPE_MUSIC,
"uuid");
AppFilter composedFilter = new CompoundFilter(ApplicationsState.FILTER_INSTANT, filter);
final ApplicationInfo info = new ApplicationInfo();
info.volumeUuid = "uuid";
info.category = ApplicationInfo.CATEGORY_AUDIO;
info.privateFlags = ApplicationInfo.PRIVATE_FLAG_INSTANT;
final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class);
appEntry.info = info;
assertThat(composedFilter.filterApp(appEntry)).isTrue();
}
@Test
public void getCompositeFilter_worksForLegacyPrivateSettings() throws Exception {
ApplicationsState.AppFilter filter =
ManageApplications.getCompositeFilter(
ManageApplications.LIST_TYPE_STORAGE,
ManageApplications.STORAGE_TYPE_LEGACY,
"uuid");
final ApplicationInfo info = new ApplicationInfo();
info.volumeUuid = "uuid";
info.category = ApplicationInfo.CATEGORY_GAME;
final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class);
appEntry.info = info;
assertThat(filter.filterApp(appEntry)).isTrue();
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.settings.backup;
import static com.google.common.truth.Truth.assertThat;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.List;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class BackupIntentTest {
private static final String INTENT_PRIVACY_SETTINGS = "android.settings.PRIVACY_SETTINGS";
private static final String BACKUP_SETTINGS_ACTIVITY =
"com.android.settings.Settings$PrivacyDashboardActivity";
private Context mContext;
@Before
public void setUp() throws Exception {
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
mContext = instrumentation.getTargetContext();
}
@Test
public void testPrivacySettingsIntentResolvesToOnlyOneActivity(){
PackageManager pm = mContext.getPackageManager();
Intent intent = new Intent(INTENT_PRIVACY_SETTINGS);
List<ResolveInfo> activities = pm.queryIntentActivities(intent, 0);
assertThat(activities).isNotNull();
assertThat(activities.size()).isEqualTo(1);
assertThat(activities.get(0).activityInfo.getComponentName().getClassName()).
isEqualTo(BACKUP_SETTINGS_ACTIVITY);
}
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright (C) 2010 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.settings.bluetooth;
import android.test.AndroidTestCase;
import android.text.InputFilter;
import android.text.SpannableStringBuilder;
import androidx.test.filters.SmallTest;
public class Utf8ByteLengthFilterTest extends AndroidTestCase {
@SmallTest
public void testFilter() {
// Define the variables
CharSequence source;
SpannableStringBuilder dest;
// Constructor to create a LengthFilter
InputFilter lengthFilter = new Utf8ByteLengthFilter(10);
InputFilter[] filters = {lengthFilter};
// filter() implicitly invoked. If the total length > filter length, the filter will
// cut off the source CharSequence from beginning to fit the filter length.
source = "abc";
dest = new SpannableStringBuilder("abcdefgh");
dest.setFilters(filters);
dest.insert(1, source);
String expectedString1 = "aabbcdefgh";
assertEquals(expectedString1, dest.toString());
dest.replace(5, 8, source);
String expectedString2 = "aabbcabcgh";
assertEquals(expectedString2, dest.toString());
dest.insert(2, source);
assertEquals(expectedString2, dest.toString());
dest.delete(1, 3);
String expectedString3 = "abcabcgh";
assertEquals(expectedString3, dest.toString());
dest.append("12345");
String expectedString4 = "abcabcgh12";
assertEquals(expectedString4, dest.toString());
source = "\u60a8\u597d"; // 2 Chinese chars == 6 bytes in UTF-8
dest.replace(8, 10, source);
assertEquals(expectedString3, dest.toString());
dest.replace(0, 1, source);
String expectedString5 = "\u60a8bcabcgh";
assertEquals(expectedString5, dest.toString());
dest.replace(0, 4, source);
String expectedString6 = "\u60a8\u597dbcgh";
assertEquals(expectedString6, dest.toString());
source = "\u00a3\u00a5"; // 2 Latin-1 chars == 4 bytes in UTF-8
dest.delete(2, 6);
dest.insert(0, source);
String expectedString7 = "\u00a3\u00a5\u60a8\u597d";
assertEquals(expectedString7, dest.toString());
dest.replace(2, 3, source);
String expectedString8 = "\u00a3\u00a5\u00a3\u597d";
assertEquals(expectedString8, dest.toString());
dest.replace(3, 4, source);
String expectedString9 = "\u00a3\u00a5\u00a3\u00a3\u00a5";
assertEquals(expectedString9, dest.toString());
// filter() explicitly invoked
dest = new SpannableStringBuilder("abcdefgh");
CharSequence beforeFilterSource = "TestLengthFilter";
String expectedAfterFilter = "TestLength";
CharSequence actualAfterFilter = lengthFilter.filter(beforeFilterSource, 0,
beforeFilterSource.length(), dest, 0, dest.length());
assertEquals(expectedAfterFilter, actualAfterFilter);
}
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.core;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.platform.test.annotations.Presubmit;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.Direction;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject2;
import android.support.test.uiautomator.Until;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.settings.development.featureflags.FeatureFlagsDashboard;
import com.android.settingslib.core.instrumentation.Instrumentable;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class LifecycleEventHandlingTest {
private static final long TIMEOUT = 2000;
private Context mContext;
private String mTargetPackage;
private UiDevice mDevice;
@Before
public void setUp() throws Exception {
mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
mDevice.wakeUp();
mDevice.executeShellCommand("wm dismiss-keyguard");
mContext = InstrumentationRegistry.getTargetContext();
mTargetPackage = mContext.getPackageName();
}
@Test
@Presubmit
@Ignore("b/133334887")
public void launchDashboard_shouldSeeFooter() {
new SubSettingLauncher(mContext)
.setDestination(FeatureFlagsDashboard.class.getName())
.setSourceMetricsCategory(Instrumentable.METRICS_CATEGORY_UNKNOWN)
.addFlags(FLAG_ACTIVITY_NEW_TASK)
.launch();
final String footerText = "Experimental";
// Scroll to bottom
final UiObject2 view = mDevice.wait(
Until.findObject(By.res(mTargetPackage, "main_content")),
TIMEOUT);
view.scroll(Direction.DOWN, 100f);
assertThat(mDevice.wait(Until.findObject(By.text(footerText)), TIMEOUT))
.isNotNull();
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright (C) 2016 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.settings.dashboard;
import android.content.res.Resources;
import android.view.View;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
/***
* Matches on the first view with id if there are multiple views using the same Id.
*/
public class FirstIdViewMatcher {
public static Matcher<View> withFirstId(final int id) {
return new TypeSafeMatcher<View>() {
Resources resources = null;
private boolean mMatched;
public void describeTo(Description description) {
String idDescription = Integer.toString(id);
if (resources != null) {
try {
idDescription = resources.getResourceName(id);
} catch (Resources.NotFoundException e) {
// No big deal, will just use the int value.
idDescription = String.format("%s (resource name not found)", id);
}
}
description.appendText("with first id: " + idDescription);
}
public boolean matchesSafely(View view) {
this.resources = view.getResources();
if (mMatched) {
return false;
} else {
mMatched = id == view.getId();
return mMatched;
}
}
};
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright (C) 2016 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.settings.dashboard;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static org.hamcrest.Matchers.allOf;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
import androidx.test.InstrumentationRegistry;
import androidx.test.espresso.matcher.ViewMatchers.Visibility;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.settings.R;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class PreferenceThemeTest {
private Instrumentation mInstrumentation;
private Context mTargetContext;
private String mTargetPackage;
@Before
public void setUp() throws Exception {
mInstrumentation = InstrumentationRegistry.getInstrumentation();
mTargetContext = mInstrumentation.getTargetContext();
mTargetPackage = mTargetContext.getPackageName();
}
@Test
public void startSetupWizardLockScreen_preferenceIconSpaceNotReserved() {
launchSetupWizardLockScreen();
// Icons should not be shown, and the frame should not occupy extra space.
onView(allOf(withId(R.id.icon_frame), withEffectiveVisibility(Visibility.VISIBLE)))
.check(doesNotExist());
onView(withId(R.id.icon_container)).check(doesNotExist());
}
private void launchSetupWizardLockScreen() {
final Intent settingsIntent = new Intent("com.android.settings.SETUP_LOCK_SCREEN")
.addCategory(Intent.CATEGORY_DEFAULT)
.setPackage(mTargetPackage)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
InstrumentationRegistry.getInstrumentation().startActivitySync(settingsIntent);
}
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.dashboard;
import static com.google.common.truth.Truth.assertThat;
import android.app.Instrumentation;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class UiBlockerControllerTest {
private static final long TIMEOUT = 600;
private static final String KEY_1 = "key1";
private static final String KEY_2 = "key2";
private Instrumentation mInstrumentation;
private UiBlockerController mSyncableController;
@Before
public void setUp() throws Exception {
mInstrumentation = InstrumentationRegistry.getInstrumentation();
mSyncableController = new UiBlockerController(Arrays.asList(KEY_1, KEY_2));
}
@Test
public void start_isSyncedReturnFalseUntilAllWorkDone() throws InterruptedException {
final CountDownLatch latch = new CountDownLatch(1);
mSyncableController.start(() -> latch.countDown());
// Return false at first
assertThat(mSyncableController.isBlockerFinished()).isFalse();
// Return false if only one job is done
mSyncableController.countDown(KEY_1);
assertThat(mSyncableController.isBlockerFinished()).isFalse();
// Return true if all jobs done
mSyncableController.countDown(KEY_2);
assertThat(latch.await(TIMEOUT, TimeUnit.MILLISECONDS)).isTrue();
assertThat(mSyncableController.isBlockerFinished()).isTrue();
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.datetime.timezone.model;
import static com.google.common.truth.Truth.assertThat;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Set;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class TimeZoneDataTest {
private TimeZoneData mTimeZoneData;
@Before
public void setUp() {
mTimeZoneData = TimeZoneData.getInstance();
}
@Test
public void lookupCountryTimeZones_shouldReturnAtLeastOneTimeZoneInEveryRegion() {
Set<String> regionIds = mTimeZoneData.getRegionIds();
for (String regionId : regionIds) {
FilteredCountryTimeZones countryTimeZones =
mTimeZoneData.lookupCountryTimeZones(regionId);
assertThat(countryTimeZones).isNotNull();
assertThat(countryTimeZones.getTimeZoneIds().size()).isGreaterThan(0);
}
}
@Test
public void lookupCountryCodesForZoneId_shouldNotReturnHiddenZone() {
/*
Simferopol is filtered out for two reasons:
1) because we specifically exclude it with the picker attribute, and
2) because it's the same as Moscow after Oct 2014.
*/
assertThat(mTimeZoneData.lookupCountryCodesForZoneId("Europe/Simferopol").isEmpty())
.isTrue();
assertThat(mTimeZoneData.lookupCountryCodesForZoneId("Europe/London").isEmpty())
.isFalse();
assertThat(mTimeZoneData.lookupCountryCodesForZoneId("America/Los_Angeles").isEmpty())
.isFalse();
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.settings.display;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.provider.Settings;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.settings.Settings.NightDisplaySettingsActivity;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class NightDisplaySettingsActivityTest {
private Context mTargetContext;
@Before
public void setUp() throws Exception {
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
mTargetContext = instrumentation.getTargetContext();
}
@Test
public void nightDisplaySettingsIntent_resolvesCorrectly() {
final boolean nightDisplayAvailable = mTargetContext.getResources().getBoolean(
com.android.internal.R.bool.config_nightDisplayAvailable);
final PackageManager pm = mTargetContext.getPackageManager();
final Intent intent = new Intent(Settings.ACTION_NIGHT_DISPLAY_SETTINGS);
final ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
if (nightDisplayAvailable) {
Assert.assertNotNull("No activity for " + Settings.ACTION_NIGHT_DISPLAY_SETTINGS, ri);
Assert.assertEquals(mTargetContext.getPackageName(), ri.activityInfo.packageName);
Assert.assertEquals(NightDisplaySettingsActivity.class.getName(),
ri.activityInfo.name);
} else {
Assert.assertNull("Should have no activity for "
+ Settings.ACTION_NIGHT_DISPLAY_SETTINGS, ri);
}
}
}

View File

@@ -0,0 +1,151 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.display;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.ContextWrapper;
import android.content.om.IOverlayManager;
import android.content.om.OverlayInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import androidx.preference.ListPreference;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import java.util.ArrayList;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class ThemePreferenceControllerTest {
private IOverlayManager mMockOverlayManager;
private ContextWrapper mContext;
private ThemePreferenceController mPreferenceController;
private PackageManager mMockPackageManager;
@Before
public void setup() {
mMockOverlayManager = mock(IOverlayManager.class);
mMockPackageManager = mock(PackageManager.class);
mContext = new ContextWrapper(InstrumentationRegistry.getTargetContext()) {
@Override
public PackageManager getPackageManager() {
return mMockPackageManager;
}
};
mPreferenceController = new ThemePreferenceController(mContext, mMockOverlayManager);
}
@Test
public void testUpdateState() throws Exception {
OverlayInfo info1 = new OverlayInfo("com.android.Theme1", "android", "",
OverlayInfo.CATEGORY_THEME, "", OverlayInfo.STATE_ENABLED, 0, 0, true);
OverlayInfo info2 = new OverlayInfo("com.android.Theme2", "android", "",
OverlayInfo.CATEGORY_THEME, "", 0, 0, 0, true);
when(mMockPackageManager.getApplicationInfo(any(), anyInt())).thenAnswer(inv -> {
ApplicationInfo info = mock(ApplicationInfo.class);
if ("com.android.Theme1".equals(inv.getArguments()[0])) {
when(info.loadLabel(any())).thenReturn("Theme1");
} else {
when(info.loadLabel(any())).thenReturn("Theme2");
}
return info;
});
when(mMockPackageManager.getPackageInfo(anyString(), anyInt())).thenReturn(
new PackageInfo());
when(mMockOverlayManager.getOverlayInfosForTarget(any(), anyInt())).thenReturn(
list(info1, info2));
ListPreference pref = mock(ListPreference.class);
mPreferenceController.updateState(pref);
ArgumentCaptor<String[]> arg = ArgumentCaptor.forClass(String[].class);
verify(pref).setEntries(arg.capture());
CharSequence[] entries = arg.getValue();
assertThat(entries).asList().containsExactly("Theme1", "Theme2");
verify(pref).setEntryValues(arg.capture());
CharSequence[] entryValues = arg.getValue();
assertThat(entryValues).asList().containsExactly(
"com.android.Theme1", "com.android.Theme2");
verify(pref).setValue(eq("com.android.Theme1"));
}
@Test
public void testUpdateState_withStaticOverlay() throws Exception {
OverlayInfo info1 = new OverlayInfo("com.android.Theme1", "android", "",
OverlayInfo.CATEGORY_THEME, "", OverlayInfo.STATE_ENABLED, 0, 0, true);
OverlayInfo info2 = new OverlayInfo("com.android.Theme2", "android", "",
OverlayInfo.CATEGORY_THEME, "", OverlayInfo.STATE_ENABLED, 0, 0, true);
when(mMockPackageManager.getApplicationInfo(any(), anyInt())).thenAnswer(inv -> {
ApplicationInfo info = mock(ApplicationInfo.class);
if ("com.android.Theme1".equals(inv.getArguments()[0])) {
when(info.loadLabel(any())).thenReturn("Theme1");
} else {
when(info.loadLabel(any())).thenReturn("Theme2");
}
return info;
});
PackageInfo pi = mock(PackageInfo.class);
when(pi.isStaticOverlayPackage()).thenReturn(true);
when(mMockPackageManager.getPackageInfo(eq("com.android.Theme1"), anyInt())).thenReturn(pi);
when(mMockPackageManager.getPackageInfo(eq("com.android.Theme2"), anyInt())).thenReturn(
new PackageInfo());
when(mMockOverlayManager.getOverlayInfosForTarget(any(), anyInt())).thenReturn(
list(info1, info2));
ListPreference pref = mock(ListPreference.class);
mPreferenceController.updateState(pref);
ArgumentCaptor<String[]> arg = ArgumentCaptor.forClass(String[].class);
verify(pref).setEntries(arg.capture());
CharSequence[] entries = arg.getValue();
assertThat(entries).asList().containsExactly("Theme2");
verify(pref).setEntryValues(arg.capture());
CharSequence[] entryValues = arg.getValue();
assertThat(entryValues).asList().containsExactly("com.android.Theme2");
verify(pref).setValue(eq("com.android.Theme2"));
}
private ArrayList<OverlayInfo> list(OverlayInfo... infos) {
ArrayList<OverlayInfo> list = new ArrayList<>();
for (OverlayInfo info : infos) {
list.add(info);
}
return list;
}
}

View File

@@ -0,0 +1,94 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.homepage.contextualcards;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.net.Uri;
import androidx.slice.Slice;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.settings.slices.CustomSliceRegistry;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.List;
@RunWith(AndroidJUnit4.class)
public class ContextualCardLoaderTest {
private static final Uri TEST_URI = Uri.parse("content://test/test");
private Context mContext;
private ContextualCardLoader mContextualCardLoader;
private EligibleCardChecker mEligibleCardChecker;
@Before
public void setUp() {
mContext = InstrumentationRegistry.getTargetContext();
mContextualCardLoader = new ContextualCardLoader(mContext);
mEligibleCardChecker = new EligibleCardChecker(mContext, getContextualCard(TEST_URI));
}
@Test
public void filterEligibleCards_twoInvalidCards_shouldReturnOneCard() {
final String sliceUri1 = "content://com.android.settings.slices/action/flashlight"; //valid
final String sliceUri2 = "content://com.android.settings.test.slices/action/flashlight";
final String sliceUri3 = "cotent://com.android.settings.slices/action/flashlight";
final List<ContextualCard> cards = new ArrayList<>();
cards.add(getContextualCard(Uri.parse(sliceUri1)));
cards.add(getContextualCard(Uri.parse(sliceUri2)));
cards.add(getContextualCard(Uri.parse(sliceUri3)));
final List<ContextualCard> result = mContextualCardLoader.filterEligibleCards(cards);
assertThat(result).hasSize(1);
}
@Test
public void bindSlice_flashlightUri_shouldReturnFlashlightSlice() {
final Slice loadedSlice =
mEligibleCardChecker.bindSlice(CustomSliceRegistry.FLASHLIGHT_SLICE_URI);
assertThat(loadedSlice.getUri()).isEqualTo(CustomSliceRegistry.FLASHLIGHT_SLICE_URI);
}
@Test
public void bindSlice_noProvider_shouldReturnNull() {
final String sliceUri = "content://com.android.settings.test.slices/action/flashlight";
final Slice loadedSlice = mEligibleCardChecker.bindSlice(Uri.parse(sliceUri));
assertThat(loadedSlice).isNull();
}
private ContextualCard getContextualCard(Uri sliceUri) {
return new ContextualCard.Builder()
.setName("test_card")
.setRankingScore(0.5f)
.setCardType(ContextualCard.CardType.SLICE)
.setSliceUri(sliceUri)
.build();
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright (C) 2020 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.settings.notification;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.allOf;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
import android.provider.Settings;
import android.support.test.uiautomator.UiDevice;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class AppBubbleNotificationSettingsTest {
private static final String WM_DISMISS_KEYGUARD_COMMAND = "wm dismiss-keyguard";
private UiDevice mUiDevice;
private Context mTargetContext;
private Instrumentation mInstrumentation;
@Before
public void setUp() throws Exception {
mInstrumentation = InstrumentationRegistry.getInstrumentation();
mTargetContext = mInstrumentation.getTargetContext();
mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
mUiDevice.wakeUp();
mUiDevice.executeShellCommand(WM_DISMISS_KEYGUARD_COMMAND);
}
@Test
public void launchBubbleNotificationSetting_shouldNotCrash() {
final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_BUBBLE_SETTINGS)
.putExtra(Settings.EXTRA_APP_PACKAGE, mTargetContext.getPackageName())
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mInstrumentation.startActivitySync(intent);
CharSequence name = mTargetContext.getApplicationInfo().loadLabel(
mTargetContext.getPackageManager());
onView(allOf(withText(name.toString()))).check(matches(isDisplayed()));
}
}

View File

@@ -0,0 +1,115 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.notification;
import static android.app.NotificationManager.IMPORTANCE_MIN;
import static android.app.NotificationManager.IMPORTANCE_NONE;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.allOf;
import static org.junit.Assert.fail;
import android.app.INotificationManager;
import android.app.Instrumentation;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.os.Process;
import android.os.ServiceManager;
import android.provider.Settings;
import android.support.test.uiautomator.UiDevice;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class ChannelNotificationSettingsTest {
private static final String WM_DISMISS_KEYGUARD_COMMAND = "wm dismiss-keyguard";
private UiDevice mUiDevice;
private Context mTargetContext;
private Instrumentation mInstrumentation;
private NotificationChannel mNotificationChannel;
private NotificationManager mNm;
@Before
public void setUp() throws Exception {
mInstrumentation = InstrumentationRegistry.getInstrumentation();
mTargetContext = mInstrumentation.getTargetContext();
mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
mUiDevice.wakeUp();
mUiDevice.executeShellCommand(WM_DISMISS_KEYGUARD_COMMAND);
mNm = (NotificationManager) mTargetContext.getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationChannel = new NotificationChannel(this.getClass().getName(),
this.getClass().getName(), IMPORTANCE_MIN);
mNm.createNotificationChannel(mNotificationChannel);
}
@Test
public void launchNotificationSetting_shouldNotCrash() {
final Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
.putExtra(Settings.EXTRA_APP_PACKAGE, mTargetContext.getPackageName())
.putExtra(Settings.EXTRA_CHANNEL_ID, mNotificationChannel.getId())
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mInstrumentation.startActivitySync(intent);
onView(allOf(withText(mNotificationChannel.getName().toString()))).check(
matches(isDisplayed()));
}
@Test
public void launchNotificationSettings_blockedChannel() throws Exception {
NotificationChannel blocked =
new NotificationChannel("blocked", "blocked", IMPORTANCE_NONE);
mNm.createNotificationChannel(blocked);
INotificationManager sINM = INotificationManager.Stub.asInterface(
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
blocked.setImportance(IMPORTANCE_NONE);
sINM.updateNotificationChannelForPackage(
mTargetContext.getPackageName(), Process.myUid(), blocked);
final Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
.putExtra(Settings.EXTRA_APP_PACKAGE, mTargetContext.getPackageName())
.putExtra(Settings.EXTRA_CHANNEL_ID, blocked.getId())
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mInstrumentation.startActivitySync(intent);
onView(allOf(withText("At your request, Android is blocking this category of notifications"
+ " from appearing on this device"))).check(matches(isDisplayed()));
try {
onView(allOf(withText("On the lock screen"))).check(matches(isDisplayed()));
fail("settings appearing for blocked channel");
} catch (Exception e) {
// expected
}
}
}

View File

@@ -0,0 +1,4 @@
# Default reviewers for this and subdirectories.
asc@google.com
dsandler@android.com
juliacr@google.com

View File

@@ -0,0 +1,121 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.password;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.pressKey;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.isEnabled;
import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.hamcrest.CoreMatchers.not;
import android.content.Context;
import android.view.KeyEvent;
import android.view.View;
import android.widget.Button;
import androidx.test.InstrumentationRegistry;
import androidx.test.espresso.action.ViewActions;
import androidx.test.espresso.matcher.ViewMatchers;
import androidx.test.filters.MediumTest;
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
import com.android.settings.R;
import com.google.android.setupcompat.PartnerCustomizationLayout;
import com.google.android.setupcompat.template.FooterBarMixin;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class SetupChooseLockPasswordAppTest {
private Context mContext;
@Rule
public ActivityTestRule<SetupChooseLockPassword> mActivityTestRule =
new ActivityTestRule<>(
SetupChooseLockPassword.class,
true /* enable touch at launch */,
false /* don't launch at every test */);
@Before
public void setUp() {
mContext = InstrumentationRegistry.getTargetContext();
}
@Test
public void testSkipDialogIsShown() throws Throwable {
SetupChooseLockPassword activity = mActivityTestRule.launchActivity(null);
PartnerCustomizationLayout layout = activity.findViewById(R.id.setup_wizard_layout);
final Button skipOrClearButton =
layout.getMixin(FooterBarMixin.class).getSecondaryButtonView();
assertThat(skipOrClearButton.getText()).isEqualTo(mContext.getString(R.string.skip_label));
assertThat(skipOrClearButton.getVisibility()).isEqualTo(View.VISIBLE);
skipOrClearButton.performClick();
assertWithMessage("Is finishing").that(activity.isFinishing()).isTrue();
}
@Test
public void clearIsNotShown_when_activityLaunchedInitially() {
SetupChooseLockPassword activity = mActivityTestRule.launchActivity(null);
PartnerCustomizationLayout layout = activity.findViewById(R.id.setup_wizard_layout);
assertThat(layout.getMixin(FooterBarMixin.class).getSecondaryButtonView().getText())
.isEqualTo(mContext.getString(R.string.lockpassword_clear_label));
}
@Test
public void clearIsNotShown_when_nothingEntered() throws Throwable {
SetupChooseLockPassword activity = mActivityTestRule.launchActivity(null);
PartnerCustomizationLayout layout = activity.findViewById(R.id.setup_wizard_layout);
onView(withId(R.id.password_entry)).perform(ViewActions.typeText("1234"))
.perform(pressKey(KeyEvent.KEYCODE_ENTER));
assertThat(
layout.getMixin(FooterBarMixin.class).getSecondaryButtonView().getVisibility())
.isEqualTo(View.GONE);
}
@Test
public void clearIsShown_when_somethingEnteredToConfirm() {
SetupChooseLockPassword activity = mActivityTestRule.launchActivity(null);
PartnerCustomizationLayout layout = activity.findViewById(R.id.setup_wizard_layout);
onView(withId(R.id.password_entry)).perform(ViewActions.typeText("1234"))
.perform(pressKey(KeyEvent.KEYCODE_ENTER));
mActivityTestRule.launchActivity(null);
onView(withId(R.id.password_entry)).perform(ViewActions.typeText("1234"))
.perform(pressKey(KeyEvent.KEYCODE_ENTER))
.perform(ViewActions.typeText("1"));
// clear should be present if text field contains content
assertThat(
layout.getMixin(FooterBarMixin.class).getSecondaryButtonView().getVisibility())
.isEqualTo(View.VISIBLE);
}
}

View File

@@ -0,0 +1,143 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.print;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assume.assumeTrue;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
import android.print.PageRange;
import android.print.PrintAttributes;
import android.print.PrintDocumentAdapter;
import android.print.PrintDocumentInfo;
import android.print.PrintJob;
import android.print.PrintManager;
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.util.Log;
import androidx.annotation.NonNull;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
import com.android.settings.Settings;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.UUID;
@RunWith(AndroidJUnit4.class)
public class PrintJobSettingsActivityTest {
private static final String EXTRA_PRINT_JOB_ID = "EXTRA_PRINT_JOB_ID";
private static final String LOG_TAG = PrintJobSettingsActivityTest.class.getSimpleName();
// Any activity is fine
@Rule
public final ActivityTestRule<Settings.PrintSettingsActivity> mActivityRule =
new ActivityTestRule<>(Settings.PrintSettingsActivity.class, true);
public static void runShellCommand(@NonNull String cmd) throws IOException {
ParcelFileDescriptor stdOut =
InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
cmd);
try (FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(stdOut)) {
byte[] buf = new byte[512];
while (fis.read(buf) != -1) {
// keep reading
}
}
}
@Before
public void requirePrintFeature() {
assumeTrue(InstrumentationRegistry.getTargetContext().getPackageManager().hasSystemFeature(
PackageManager.FEATURE_PRINTING));
}
@Before
public void wakeUpScreen() throws Exception {
runShellCommand("input keyevent KEYCODE_WAKEUP");
}
@Test
@LargeTest
public void viewPrintJobSettings() throws Exception {
UUID uuid = UUID.randomUUID();
Object isWriteCalled = new Object();
// Create adapter that is good enough to start a print preview
PrintDocumentAdapter adapter = new PrintDocumentAdapter() {
@Override
public void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
CancellationSignal cancellationSignal,
LayoutResultCallback callback, Bundle extras) {
callback.onLayoutFinished(new PrintDocumentInfo.Builder(uuid.toString()).build(),
true);
}
@Override
public void onWrite(PageRange[] pages, ParcelFileDescriptor destination,
CancellationSignal cancellationSignal,
WriteResultCallback callback) {
synchronized (isWriteCalled) {
isWriteCalled.notify();
}
callback.onWriteFailed(null);
}
};
Activity activity = mActivityRule.getActivity();
PrintManager pm = mActivityRule.getActivity().getSystemService(PrintManager.class);
// Start printing
PrintJob printJob = pm.print(uuid.toString(), adapter, null);
// Wait until print preview is up
synchronized (isWriteCalled) {
isWriteCalled.wait();
}
// Start print job settings
Intent intent = new Intent(android.provider.Settings.ACTION_PRINT_SETTINGS);
intent.putExtra(EXTRA_PRINT_JOB_ID, printJob.getId().flattenToString());
intent.setData(Uri.fromParts("printjob", printJob.getId().flattenToString(), null));
activity.startActivity(intent);
UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
UiObject2 printPrefTitle = uiDevice.wait(Until.findObject(By.text("Configuring "
+ uuid.toString())), 5000);
assertNotNull(printPrefTitle);
Log.i(LOG_TAG, "Found " + printPrefTitle.getText());
}
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright (C) 2016 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.settings.search;
import static com.google.common.truth.Truth.assertThat;
import android.provider.SearchIndexablesContract;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class SearchIndexablesContractTest {
@Test
public void testRawColumns_matchContractIndexing() {
assertThat(SearchIndexablesContract.RawData.COLUMN_RANK)
.isEqualTo(SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[0]);
assertThat(SearchIndexablesContract.RawData.COLUMN_TITLE)
.isEqualTo(SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[1]);
assertThat(SearchIndexablesContract.RawData.COLUMN_SUMMARY_ON)
.isEqualTo(SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[2]);
assertThat(SearchIndexablesContract.RawData.COLUMN_SUMMARY_OFF)
.isEqualTo(SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[3]);
assertThat(SearchIndexablesContract.RawData.COLUMN_ENTRIES)
.isEqualTo(SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[4]);
assertThat(SearchIndexablesContract.RawData.COLUMN_KEYWORDS)
.isEqualTo(SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[5]);
assertThat(SearchIndexablesContract.RawData.COLUMN_SCREEN_TITLE)
.isEqualTo(SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[6]);
assertThat(SearchIndexablesContract.RawData.COLUMN_CLASS_NAME)
.isEqualTo(SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[7]);
assertThat(SearchIndexablesContract.RawData.COLUMN_ICON_RESID)
.isEqualTo(SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[8]);
assertThat(SearchIndexablesContract.RawData.COLUMN_INTENT_ACTION)
.isEqualTo(SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[9]);
assertThat(SearchIndexablesContract.RawData.COLUMN_INTENT_TARGET_PACKAGE)
.isEqualTo(SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[10]);
assertThat(SearchIndexablesContract.RawData.COLUMN_INTENT_TARGET_CLASS)
.isEqualTo(SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[11]);
assertThat(SearchIndexablesContract.RawData.COLUMN_KEY)
.isEqualTo(SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[12]);
assertThat(SearchIndexablesContract.RawData.COLUMN_USER_ID)
.isEqualTo(SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[13]);
assertThat(SearchIndexablesContract.RawData.PAYLOAD_TYPE)
.isEqualTo(SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[14]);
assertThat(SearchIndexablesContract.RawData.PAYLOAD)
.isEqualTo(SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[15]);
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.search;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class SearchResultTrampolineTest {
private Context mContext;
@Before
public void setUp() {
mContext = InstrumentationRegistry.getTargetContext();
}
@Test
public void canLaunchSettingsTrampolineWithIntentAction() {
final PackageManager pm = mContext.getPackageManager();
final ResolveInfo info =
pm.resolveActivity(new Intent("com.android.settings.SEARCH_RESULT_TRAMPOLINE"), 0);
assertThat(info.activityInfo.name)
.isEqualTo(SearchResultTrampoline.class.getName());
}
}

View File

@@ -0,0 +1,86 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.search;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.platform.test.annotations.Presubmit;
import android.provider.SearchIndexablesContract;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class SettingsSearchIndexablesProviderTest {
private Context mContext;
@Before
public void setUp() {
mContext = InstrumentationRegistry.getTargetContext();
}
@After
public void cleanUp() {
System.clearProperty(SettingsSearchIndexablesProvider.SYSPROP_CRASH_ON_ERROR);
}
@Test
public void testSiteMapPairsFetched() {
final Uri uri = Uri.parse("content://" + mContext.getPackageName() + "/" +
SearchIndexablesContract.SITE_MAP_PAIRS_PATH);
final Cursor cursor = mContext.getContentResolver().query(uri, null, null, null, null);
final int size = cursor.getCount();
assertThat(size).isGreaterThan(0);
while (cursor.moveToNext()) {
assertThat(cursor.getString(cursor.getColumnIndexOrThrow(
SearchIndexablesContract.SiteMapColumns.PARENT_CLASS)))
.isNotEmpty();
assertThat(cursor.getString(cursor.getColumnIndexOrThrow(
SearchIndexablesContract.SiteMapColumns.CHILD_CLASS)))
.isNotEmpty();
}
}
/**
* All {@link Indexable.SearchIndexProvider} should collect a list of non-indexable keys
* without crashing. This test enables crashing of individual providers in the indexing pipeline
* and checks that there are no crashes.
*/
@Test
@Presubmit
public void nonIndexableKeys_shouldNotCrash() {
// Allow crashes in the indexing pipeline.
System.setProperty(SettingsSearchIndexablesProvider.SYSPROP_CRASH_ON_ERROR,
"enabled");
final Uri uri = Uri.parse("content://" + mContext.getPackageName() + "/" +
SearchIndexablesContract.NON_INDEXABLES_KEYS_PATH);
mContext.getContentResolver().query(uri, null, null, null, null);
}
}

View File

@@ -0,0 +1 @@
include /src/com/android/settings/sim/OWNERS

View File

@@ -0,0 +1,179 @@
/*
* Copyright (C) 2009 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.settings.tests;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import com.android.settings.tests.unit.R;
public class BluetoothRequestPermissionTest extends Activity {
private static final String TAG = "BluetoothRequestPermissionTest";
BluetoothAdapter mAdapter;
private ArrayAdapter<String> mMsgAdapter;
// Discoverable button alternates between 20 second timeout and no timeout.
private boolean mDiscoveryWithTimeout = true;
private class BtOnClickListener implements OnClickListener {
final boolean mEnableOnly; // enable or enable + discoverable
public BtOnClickListener(boolean enableOnly) {
mEnableOnly = enableOnly;
}
public void onClick(View v) {
requestPermission(mEnableOnly);
}
}
private class BtScanOnClickListener implements OnClickListener {
public void onClick(View v) {
Button scanButton = (Button) v;
if (mAdapter.isDiscovering()) {
mAdapter.cancelDiscovery();
scanButton.setText(R.string.start_scan);
} else {
mAdapter.startDiscovery();
scanButton.setText(R.string.stop_scan);
}
}
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.bluetooth_request_permission_test);
mAdapter = BluetoothAdapter.getDefaultAdapter();
Button enable = (Button) findViewById(R.id.enable);
enable.setOnClickListener(new BtOnClickListener(true /* enable */));
Button discoverable = (Button) findViewById(R.id.discoverable);
discoverable.setOnClickListener(new BtOnClickListener(false /* enable & discoverable */));
Button scanButton = (Button) findViewById(R.id.scan);
scanButton.setOnClickListener(new BtScanOnClickListener());
if (mAdapter.isDiscovering()) {
scanButton.setText(R.string.stop_scan);
} else {
scanButton.setText(R.string.start_scan);
}
mMsgAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1);
ListView listView = (ListView) findViewById(R.id.msg_container);
listView.setAdapter(mMsgAdapter);
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
filter.addAction(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver, filter);
addMsg("Initialized");
}
void requestPermission(boolean enableOnly) {
Intent i = new Intent();
if (enableOnly) {
addMsg("Starting activity to enable bt");
i.setAction(BluetoothAdapter.ACTION_REQUEST_ENABLE);
} else {
addMsg("Starting activity to enable bt + discovery");
i.setAction(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
// Discoverability duration toggles between 20 seconds and no timeout.
int timeout = (mDiscoveryWithTimeout ? 20 : 0);
i.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, timeout);
mDiscoveryWithTimeout = !mDiscoveryWithTimeout;
}
startActivityForResult(i, 1);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode != 1) {
Log.e(TAG, "Unexpected onActivityResult " + requestCode + " " + resultCode);
return;
}
if (resultCode == Activity.RESULT_CANCELED) {
addMsg("Result = RESULT_CANCELED");
} else if (resultCode == Activity.RESULT_OK) {
addMsg("Result = RESULT_OK (not expected for discovery)");
} else {
addMsg("Result = " + resultCode);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(mReceiver);
}
private void addMsg(String msg) {
mMsgAdapter.add(msg);
Log.d(TAG, "msg");
}
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent == null)
return;
String action = intent.getAction();
if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
String stateStr = "???";
switch (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothDevice.ERROR)) {
case BluetoothAdapter.STATE_OFF:
stateStr = "off";
break;
case BluetoothAdapter.STATE_TURNING_ON:
stateStr = "turning on";
break;
case BluetoothAdapter.STATE_ON:
stateStr = "on";
break;
case BluetoothAdapter.STATE_TURNING_OFF:
stateStr = "turning off";
break;
}
addMsg("Bluetooth status = " + stateStr);
} else if (action.equals(BluetoothDevice.ACTION_FOUND)) {
String name = intent.getStringExtra(BluetoothDevice.EXTRA_NAME);
addMsg("Found: " + name);
} else if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_STARTED)) {
addMsg("Scan started...");
} else if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) {
addMsg("Scan ended");
}
}
};
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright (C) 2010 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.settings.tests;
import android.app.Activity;
import android.os.Bundle;
import com.android.settings.tests.unit.R;
public class Manufacturer extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.manufacturer_main);
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (C) 2010 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.settings.tests;
import android.app.Activity;
import android.os.Bundle;
import com.android.settings.tests.unit.R;
public class Operator extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.operator_main);
}
}

View File

@@ -0,0 +1,111 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.utils;
import static com.android.settings.utils.FileSizeFormatter.GIGABYTE_IN_BYTES;
import static com.android.settings.utils.FileSizeFormatter.MEGABYTE_IN_BYTES;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.icu.util.MeasureUnit;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class FileSizeFormatterTest {
private Context mContext;
@Before
public void setUp() throws Exception {
mContext = InstrumentationRegistry.getTargetContext();
}
@Test
public void formatFileSize_zero() throws Exception {
assertThat(
FileSizeFormatter.formatFileSize(
mContext,
0 /* size */,
MeasureUnit.GIGABYTE,
GIGABYTE_IN_BYTES))
.isEqualTo("0.00 GB");
}
@Test
public void formatFileSize_smallSize() throws Exception {
assertThat(
FileSizeFormatter.formatFileSize(
mContext,
MEGABYTE_IN_BYTES * 11 /* size */,
MeasureUnit.GIGABYTE,
GIGABYTE_IN_BYTES))
.isEqualTo("0.01 GB");
}
@Test
public void formatFileSize_lessThanOneSize() throws Exception {
assertThat(
FileSizeFormatter.formatFileSize(
mContext,
MEGABYTE_IN_BYTES * 155 /* size */,
MeasureUnit.GIGABYTE,
GIGABYTE_IN_BYTES))
.isEqualTo("0.16 GB");
}
@Test
public void formatFileSize_greaterThanOneSize() throws Exception {
assertThat(
FileSizeFormatter.formatFileSize(
mContext,
MEGABYTE_IN_BYTES * 1551 /* size */,
MeasureUnit.GIGABYTE,
GIGABYTE_IN_BYTES))
.isEqualTo("1.6 GB");
}
@Test
public void formatFileSize_greaterThanTen() throws Exception {
// Should round down due to truncation
assertThat(
FileSizeFormatter.formatFileSize(
mContext,
GIGABYTE_IN_BYTES * 15 + MEGABYTE_IN_BYTES * 50 /* size */,
MeasureUnit.GIGABYTE,
GIGABYTE_IN_BYTES))
.isEqualTo("15 GB");
}
@Test
public void formatFileSize_handlesNegativeFileSizes() throws Exception {
assertThat(
FileSizeFormatter.formatFileSize(
mContext,
MEGABYTE_IN_BYTES * -155 /* size */,
MeasureUnit.GIGABYTE,
GIGABYTE_IN_BYTES))
.isEqualTo("-0.16 GB");
}
}

View File

@@ -0,0 +1,106 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.testutils;
import android.os.IBinder;
import android.os.ServiceManager;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
// This class is for replacing existing system service with the mocked service.
// Copied from CellBroadcastReceiver app.
public final class MockedServiceManager {
private final String TAG = MockedServiceManager.class.getSimpleName();
private final HashMap<String, IBinder> mServiceManagerMockedServices = new HashMap<>();
private final HashMap<InstanceKey, Object> mOldInstances = new HashMap<>();
private final LinkedList<InstanceKey> mInstanceKeys = new LinkedList<>();
private static class InstanceKey {
final Class mClass;
final String mInstName;
final Object mObj;
InstanceKey(final Class c, final String instName, final Object obj) {
mClass = c;
mInstName = instName;
mObj = obj;
}
@Override
public int hashCode() {
return (mClass.getName().hashCode() * 31 + mInstName.hashCode()) * 31;
}
@Override
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != getClass()) {
return false;
}
InstanceKey other = (InstanceKey) obj;
return (other.mClass == mClass && other.mInstName.equals(mInstName)
&& other.mObj == mObj);
}
}
public MockedServiceManager() throws Exception {
replaceInstance(ServiceManager.class, "sCache", null, mServiceManagerMockedServices);
}
public void replaceService(String key, IBinder binder) {
mServiceManagerMockedServices.put(key, binder);
}
public void restoreAllServices() throws Exception {
restoreInstances();
}
public synchronized void replaceInstance(final Class c, final String instanceName,
final Object obj, final Object newValue)
throws Exception {
Field field = c.getDeclaredField(instanceName);
field.setAccessible(true);
InstanceKey key = new InstanceKey(c, instanceName, obj);
if (!mOldInstances.containsKey(key)) {
mOldInstances.put(key, field.get(obj));
mInstanceKeys.add(key);
}
field.set(obj, newValue);
}
public synchronized void restoreInstances() throws Exception {
Iterator<InstanceKey> it = mInstanceKeys.descendingIterator();
while (it.hasNext()) {
InstanceKey key = it.next();
Field field = key.mClass.getDeclaredField(key.mInstName);
field.setAccessible(true);
field.set(key.mObj, mOldInstances.get(key));
}
mInstanceKeys.clear();
mOldInstances.clear();
}
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright (C) 2016 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.settings.vpn2;
import static com.android.settings.vpn2.AppManagementFragment.appHasVpnPermission;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.os.Process;
import android.test.AndroidTestCase;
import androidx.test.filters.SmallTest;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
public class AppSettingsTest extends AndroidTestCase {
private static final String TAG = AppSettingsTest.class.getSimpleName();
@Mock private Context mContext;
@Mock private AppOpsManager mAppOps;
@Override
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
when(mContext.getSystemService(eq(Context.APP_OPS_SERVICE))).thenReturn(mAppOps);
}
@SmallTest
public void testAppOpsRequiredToOpenFragment() {
ApplicationInfo mockApp = createMockApp();
final AppOpsManager.PackageOps[] blankOps = {
new AppOpsManager.PackageOps(mockApp.packageName, mockApp.uid, new ArrayList<>()),
new AppOpsManager.PackageOps(mockApp.packageName, mockApp.uid, new ArrayList<>())
};
// List with one package op
when(mAppOps.getOpsForPackage(eq(mockApp.uid), eq(mockApp.packageName),
any(int[].class))).thenReturn(Arrays.asList(
new AppOpsManager.PackageOps[] {blankOps[0]}));
assertTrue(appHasVpnPermission(mContext, mockApp));
// List with more than one package op
when(mAppOps.getOpsForPackage(eq(mockApp.uid), eq(mockApp.packageName),
any(int[].class))).thenReturn(Arrays.asList(blankOps));
assertTrue(appHasVpnPermission(mContext, mockApp));
// Empty list
when(mAppOps.getOpsForPackage(eq(mockApp.uid), eq(mockApp.packageName),
any(int[].class))).thenReturn(Collections.emptyList());
assertFalse(appHasVpnPermission(mContext, mockApp));
// Null list (may be returned in place of an empty list)
when(mAppOps.getOpsForPackage(eq(mockApp.uid), eq(mockApp.packageName),
any(int[].class))).thenReturn(null);
assertFalse(appHasVpnPermission(mContext, mockApp));
}
private static ApplicationInfo createMockApp() {
final ApplicationInfo app = new ApplicationInfo();
app.packageName = "com.example.mockvpn";
app.uid = Process.FIRST_APPLICATION_UID;
return app;
}
}

View File

@@ -0,0 +1,161 @@
/*
* Copyright (C) 2016 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.settings.vpn2;
import static org.mockito.AdditionalMatchers.not;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.argThat;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
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.test.AndroidTestCase;
import android.text.TextUtils;
import androidx.test.filters.SmallTest;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnProfile;
import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class PreferenceListTest extends AndroidTestCase {
private static final String TAG = "PreferenceListTest";
@Mock VpnSettings mSettings;
final Map<String, LegacyVpnPreference> mLegacyMocks = new HashMap<>();
final Map<AppVpnInfo, AppPreference> mAppMocks = new HashMap<>();
@Override
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mLegacyMocks.clear();
mAppMocks.clear();
doAnswer(invocation -> {
final String key = ((VpnProfile)(invocation.getArguments()[0])).key;
if (!mLegacyMocks.containsKey(key)) {
mLegacyMocks.put(key, mock(LegacyVpnPreference.class));
}
return mLegacyMocks.get(key);
}).when(mSettings).findOrCreatePreference(any(VpnProfile.class), anyBoolean());
doAnswer(invocation -> {
final AppVpnInfo key = (AppVpnInfo)(invocation.getArguments()[0]);
if (!mAppMocks.containsKey(key)) {
mAppMocks.put(key, mock(AppPreference.class));
}
return mAppMocks.get(key);
}).when(mSettings).findOrCreatePreference(any(AppVpnInfo.class));
doNothing().when(mSettings).setShownPreferences(any());
doReturn(true).when(mSettings).canAddPreferences();
}
@SmallTest
public void testNothingShownByDefault() {
final VpnSettings.UpdatePreferences updater = new VpnSettings.UpdatePreferences(mSettings);
updater.run();
verify(mSettings, never()).findOrCreatePreference(any(VpnProfile.class), anyBoolean());
assertEquals(0, mLegacyMocks.size());
assertEquals(0, mAppMocks.size());
}
@SmallTest
public void testDisconnectedLegacyVpnShown() {
final VpnProfile vpnProfile = new VpnProfile("test-disconnected");
final VpnSettings.UpdatePreferences updater = new VpnSettings.UpdatePreferences(mSettings);
updater.legacyVpns(
/* vpnProfiles */ Collections.<VpnProfile>singletonList(vpnProfile),
/* connectedLegacyVpns */ Collections.<String, LegacyVpnInfo>emptyMap(),
/* lockdownVpnKey */ null);
updater.run();
verify(mSettings, times(1)).findOrCreatePreference(any(VpnProfile.class), eq(true));
assertEquals(1, mLegacyMocks.size());
assertEquals(0, mAppMocks.size());
}
@SmallTest
public void testConnectedLegacyVpnShownIfDeleted() {
final LegacyVpnInfo connectedLegacyVpn =new LegacyVpnInfo();
connectedLegacyVpn.key = "test-connected";
final VpnSettings.UpdatePreferences updater = new VpnSettings.UpdatePreferences(mSettings);
updater.legacyVpns(
/* vpnProfiles */ Collections.<VpnProfile>emptyList(),
/* connectedLegacyVpns */ new HashMap<String, LegacyVpnInfo>() {{
put(connectedLegacyVpn.key, connectedLegacyVpn);
}},
/* lockdownVpnKey */ null);
updater.run();
verify(mSettings, times(1)).findOrCreatePreference(any(VpnProfile.class), eq(false));
assertEquals(1, mLegacyMocks.size());
assertEquals(0, mAppMocks.size());
}
@SmallTest
public void testConnectedLegacyVpnShownExactlyOnce() {
final VpnProfile vpnProfile = new VpnProfile("test-no-duplicates");
final LegacyVpnInfo connectedLegacyVpn = new LegacyVpnInfo();
connectedLegacyVpn.key = new String(vpnProfile.key);
final VpnSettings.UpdatePreferences updater = new VpnSettings.UpdatePreferences(mSettings);
updater.legacyVpns(
/* vpnProfiles */ Collections.<VpnProfile>singletonList(vpnProfile),
/* connectedLegacyVpns */ new HashMap<String, LegacyVpnInfo>() {{
put(connectedLegacyVpn.key, connectedLegacyVpn);
}},
/* lockdownVpnKey */ null);
updater.run();
final ArgumentMatcher<VpnProfile> equalsFake = arg -> {
if (arg == vpnProfile) return true;
if (arg == null) return false;
return TextUtils.equals(arg.key, vpnProfile.key);
};
// The VPN profile should have been used to create a preference and set up at laest once
// with update=true to fill in all the fields.
verify(mSettings, atLeast(1)).findOrCreatePreference(argThat(equalsFake), eq(true));
// ...But no other VPN profile key should ever have been passed in.
verify(mSettings, never()).findOrCreatePreference(not(argThat(equalsFake)), anyBoolean());
// And so we should still have exactly 1 preference created.
assertEquals(1, mLegacyMocks.size());
assertEquals(0, mAppMocks.size());
}
}

View File

@@ -0,0 +1 @@
include /src/com/android/settings/wifi/calling/OWNERS

View File

@@ -0,0 +1,32 @@
package {
default_team: "trendy_team_android_settings_app",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "packages_apps_Settings_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["packages_apps_Settings_license"],
}
android_test {
name: "SettingsPerfTests",
certificate: "platform",
libs: [
"android.test.runner",
],
static_libs: [
"androidx.test.rules",
"androidx.test.uiautomator_uiautomator",
],
// Include all test java files.
srcs: ["src/**/*.java"],
platform_apis: true,
test_suites: ["device-tests"],
instrumentation_for: "Settings",
}

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2020 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.settings.tests.perf">
<application>
<uses-library android:name="android.test.runner" />
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.settings.tests.perf"
android:label="Settings Performance Test Cases">
</instrumentation>
</manifest>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2020 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 Settings Performance Test Cases.">
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="apct-instrumentation" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="SettingsPerfTests.apk" />
</target_preparer>
<option name="test-tag" value="SettingsPerfTests" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.settings.tests.perf" />
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
<option name="hidden-api-checks" value="false"/>
</test>
</configuration>

View File

@@ -0,0 +1,236 @@
/*
* Copyright (C) 2020 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.settings.tests.perf;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static junit.framework.TestCase.fail;
import android.app.Instrumentation;
import android.os.Bundle;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.UiDevice;
import androidx.test.uiautomator.UiSelector;
import androidx.test.uiautomator.Until;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Collections;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@RunWith(AndroidJUnit4.class)
public class LaunchSettingsTest {
private static class Page {
String action;
String displayName;
String title;
Page(String action, String displayName, String title) {
this.action = action;
this.displayName = displayName;
this.title = title;
}
}
private static final String SCREEN_TIME_OUT = "7200000";
private static final String DEFAULT_SCREEN_TIMEOUT = "15000";
private static final int TIME_OUT = 5000;
private static final int TEST_TIME = 10;
private static final Pattern PATTERN = Pattern.compile("TotalTime:\\s[0-9]*");
private static final Page[] PAGES;
private static final String TAG = "SettingsPerfTests";
static {
PAGES = new Page[]{
new Page("android.settings.SETTINGS", "Search settings", "Settings"),
new Page("android.settings.WIFI_SETTINGS", "Use WiFi", "Wi-Fi"),
new Page("android.settings.BLUETOOTH_SETTINGS", "Connected devices", "BlueTooth"),
new Page("android.settings.APPLICATION_SETTINGS", "App info", "Application"),
new Page("android.intent.action.POWER_USAGE_SUMMARY", "Battery", "Battery"),
new Page("android.settings.INTERNAL_STORAGE_SETTINGS", "Storage", "Storage")
};
}
private Bundle mBundle;
private UiDevice mDevice;
private Instrumentation mInstrumentation;
private Map<String, ArrayList<Integer>> mResult;
private String mDefaultScreenTimeout;
private String mDefaultAirplaneModeStatus;
@Before
public void setUp() throws Exception {
mBundle = new Bundle();
mDevice = UiDevice.getInstance(getInstrumentation());
mInstrumentation = InstrumentationRegistry.getInstrumentation();
mResult = new LinkedHashMap<>();
mDefaultScreenTimeout = mDevice.executeShellCommand(
"settings get system screen_off_timeout");
mDefaultAirplaneModeStatus = getAirplaneModeStatus();
setScreenTimeOut(SCREEN_TIME_OUT);
setAirplaneMode();
mDevice.pressHome();
mDevice.waitForIdle(TIME_OUT);
for (Page page : PAGES) {
mResult.put(page.title, new ArrayList<Integer>());
}
}
@After
public void tearDown() throws Exception {
putResultToBundle();
mInstrumentation.sendStatus(0, mBundle);
resetScreenTimeout();
resetAirplaneMode();
closeApp();
}
@Test
public void settingsPerformanceTest() throws Exception {
for (int i = 0; i < TEST_TIME; i++) {
for (Page page : PAGES) {
executePreformanceTest(page.action, page.displayName, page.title);
}
}
}
private void executePreformanceTest(String action, String displayName, String title)
throws Exception {
closeApp();
mDevice.waitForIdle(TIME_OUT);
final String mString = mDevice.executeShellCommand("am start -W -a" + action);
mDevice.wait(Until.findObject(By.text(displayName)), TIME_OUT);
handleLaunchResult(title, mString);
}
private void handleLaunchResult(String title, String shellCommandResult) {
Matcher mMatcher = PATTERN.matcher(shellCommandResult);
if (mMatcher.find()) {
mResult.get(title).add(Integer.valueOf(mMatcher.group().split("\\s")[1]));
} else {
fail(String.format("Not found %s.\n %s", title, shellCommandResult));
}
}
private void closeApp() throws Exception {
mDevice.executeShellCommand("am force-stop com.android.settings");
Thread.sleep(1000);
}
private void putResultToBundle() {
for (String string : mResult.keySet()) {
mBundle.putString(String.format("LaunchSettingsTest_%s_%s", string, "max"),
getMax(string));
mBundle.putString(String.format("LaunchSettingsTest_%s_%s", string, "min"),
getMin(string));
mBundle.putString(String.format("LaunchSettingsTest_%s_%s", string, "avg"),
getAvg(string));
mBundle.putString(String.format("LaunchSettingsTest_%s_%s", string, "25 Percentile"),
getPercentile(string, 25));
mBundle.putString(String.format("LaunchSettingsTest_%s_%s", string, "50 Percentile"),
getPercentile(string, 50));
mBundle.putString(String.format("LaunchSettingsTest_%s_%s", string, "75 Percentile"),
getPercentile(string, 75));
mBundle.putString(String.format("LaunchSettingsTest_%s_%s", string, "all_results"),
mResult.get(string).toString());
mBundle.putString(String.format("LaunchSettingsTest_%s_%s", string, "results_count"),
String.valueOf(mResult.get(string).size()));
}
}
private String getMax(String page) {
if (mResult.get(page).size() == TEST_TIME) {
return String.format("%s", Collections.max(mResult.get(page)));
}
Log.e(TAG, String.format("Fail to get max of %s.", page));
return "0";
}
private String getMin(String page) {
if (mResult.get(page).size() == TEST_TIME) {
return String.format("%s", Collections.min(mResult.get(page)));
}
Log.e(TAG, String.format("Fail to get min of %s.", page));
return "0";
}
private String getAvg(String page) {
if (mResult.get(page).size() == TEST_TIME) {
return String.valueOf((int) mResult.get(page).stream().mapToInt(
i -> i).average().orElse(0));
}
Log.e(TAG, String.format("Fail to get avg of %s.", page));
return "0";
}
private void setScreenTimeOut(String timeout) throws Exception {
mDevice.executeShellCommand("settings put system screen_off_timeout " + timeout);
}
private void resetScreenTimeout() throws Exception {
String timeout = DEFAULT_SCREEN_TIMEOUT;
if (!mDefaultScreenTimeout.isEmpty()) {
timeout = mDefaultScreenTimeout;
}
setScreenTimeOut(timeout);
}
private void setAirplaneMode() throws Exception {
if (mDefaultAirplaneModeStatus.equals("0\n")) {
clickAirplaneMode();
}
}
private void resetAirplaneMode() throws Exception {
if (!getAirplaneModeStatus().equals(mDefaultAirplaneModeStatus)) {
clickAirplaneMode();
}
}
private void clickAirplaneMode() throws Exception {
mDevice.executeShellCommand("am start -W -a android.settings.AIRPLANE_MODE_SETTINGS");
mDevice.waitForIdle(TIME_OUT);
mDevice.findObject(By.textContains("Airplane")).click();
mDevice.waitForIdle(TIME_OUT);
}
private String getAirplaneModeStatus() throws Exception {
return mDevice.executeShellCommand("settings get global airplane_mode_on");
}
private String getPercentile(String page, double position) {
Collections.sort(mResult.get(page));
if (mResult.get(page).size() == TEST_TIME) {
return String.valueOf(
mResult.get(page).get((int) (Math.ceil(TEST_TIME * position / 100)) - 1));
}
Log.e(TAG, String.format("Fail to get percentile of %s.", page));
return "0";
}
}

View File

@@ -0,0 +1,112 @@
package {
default_team: "trendy_team_android_settings_app",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "packages_apps_Settings_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["packages_apps_Settings_license"],
}
// Build SettingsRoboTestStub.apk which includes test-only resources.
android_app {
name: "SettingsRoboTestStub",
defaults: [
"SettingsLibDefaults",
"SettingsLib-search-defaults",
],
platform_apis: true,
certificate: "platform",
privileged: true,
use_resource_processor: true,
resource_dirs: ["res"],
static_libs: [
"Settings-core",
"androidx.fragment_fragment-testing",
"frameworks-base-testutils",
"androidx.fragment_fragment",
"androidx.lifecycle_lifecycle-runtime-testing",
"kotlinx_coroutines_test",
],
aaptflags: ["--extra-packages com.android.settings"],
libs: [
"telephony-common",
"ims-common",
],
uses_libs: ["org.apache.http.legacy"],
optional_uses_libs: [
"androidx.window.extensions",
"androidx.window.sidecar",
],
}
// Settings Robolectric test target.
android_robolectric_test {
name: "SettingsRoboTests",
srcs: [
"src/**/*.java",
"src/**/*.kt",
],
// test_suites attribute is not needed. This module will be configured in ATP GCL file.
static_libs: [
"Robolectric_shadows_androidx_fragment_upstream",
"Settings_robolectric_meta_service_file",
"SettingsLib-robo-testutils",
"Settings-robo-testutils",
"android.webkit.flags-aconfig-java",
"androidx.test.core",
"androidx.test.espresso.core",
"androidx.test.ext.junit",
"androidx.test.rules",
"androidx.test.runner",
"flag-junit",
"flag-junit-base",
"aconfig_settings_flags_lib",
"platform-test-annotations",
"Settings-testutils2",
"notification_flags_lib",
"com_android_server_accessibility_flags_lib",
"testables",
],
libs: [
"ims-common",
"android.test.mock",
],
java_resource_dirs: [
"config",
"resources",
],
instrumentation_for: "SettingsRoboTestStub",
test_options: {
timeout: 36000,
shards: 10,
},
coverage_libs: [
"Settings-core",
"SettingsLib",
"SettingsLib-search",
],
upstream: true,
}
java_library {
name: "Settings-robo-testutils",
srcs: ["testutils/**/*.java"],
libs: [
"Robolectric_all-target_upstream",
"Settings-core",
"mockito-robolectric-prebuilt",
"truth",
],
}

View File

@@ -0,0 +1,28 @@
<?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"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
coreApp="true"
package="com.android.settings">
<uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
<application>
<activity android:name="com.android.settings.security.TestActivity" android:exported="true" />
</application>
</manifest>

View File

@@ -0,0 +1,2 @@
# We do not guard tests - everyone is welcomed to contribute to tests.
per-file *.java=*

View File

@@ -0,0 +1,24 @@
# Running Settings Robolectric tests
## The full suite
```
$ croot
$ make RunSettingsRoboTests
```
## Running a single test class
```
$ croot
$ make RunSettingsRoboTests ROBOTEST_FILTER=<ClassName>
```
For example:
```
make RunSettingsRoboTests ROBOTEST_FILTER=CodeInspectionTest
```
You can also use partial class name in ROBOTEST_FILTER. If the partial class name matches
multiple file names, all of them will be executed.

Binary file not shown.

View File

@@ -0,0 +1,14 @@
com.android.settings.applications.appinfo.AppButtonsPreferenceController
com.android.settings.applications.appinfo.AppBatteryPreferenceController
com.android.settings.applications.appinfo.AppHeaderViewPreferenceController
com.android.settings.applications.appinfo.AppMemoryPreferenceController
com.android.settings.applications.appinfo.InstantAppButtonsPreferenceController
com.android.settings.bluetooth.BluetoothDeviceNamePreferenceController
com.android.settings.bluetooth.BluetoothDeviceRenamePreferenceController
com.android.settings.datausage.DataUsageSummaryPreferenceController
com.android.settings.datausage.WifiDataUsageSummaryPreferenceController
com.android.settings.fuelgauge.RestrictAppPreferenceController
com.android.settings.fuelgauge.batterysaver.BatterySaverButtonPreferenceController
com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController
com.android.settings.security.VisiblePatternProfilePreferenceController
com.android.settings.wifi.details2.WifiMeteredPreferenceController2

View File

@@ -0,0 +1,94 @@
com.android.settings.accessibility.AccessibilitySettingsForSetupWizard
com.android.settings.accessibility.ToggleAccessibilityServicePreferenceFragment
com.android.settings.accessibility.ToggleScreenMagnificationPreferenceFragment
com.android.settings.accessibility.ToggleScreenMagnificationPreferenceFragmentForSetupWizard
com.android.settings.accessibility.ToggleScreenReaderPreferenceFragmentForSetupWizard
com.android.settings.accessibility.ToggleSelectToSpeakPreferenceFragmentForSetupWizard
com.android.settings.accounts.AccountDetailDashboardFragment
com.android.settings.accounts.AccountPersonalDashboardFragment
com.android.settings.accounts.AccountWorkProfileDashboardFragment
com.android.settings.accounts.AccountSyncSettings
com.android.settings.accounts.ChooseAccountFragment
com.android.settings.applications.appinfo.AppInfoDashboardFragment
com.android.settings.applications.appinfo.DrawOverlayDetails
com.android.settings.applications.appinfo.ExternalSourcesDetails
com.android.settings.applications.appinfo.WriteSettingsDetails
com.android.settings.applications.AppLaunchSettings
com.android.settings.applications.AppStorageSettings
com.android.settings.applications.ProcessStatsDetail
com.android.settings.applications.ProcessStatsSummary
com.android.settings.applications.ProcessStatsUi
com.android.settings.applications.RunningServices
com.android.settings.applications.specialaccess.pictureinpicture.PictureInPictureDetails
com.android.settings.applications.specialaccess.zenaccess.ZenAccessDetails
com.android.settings.applications.UsageAccessDetails
com.android.settings.backup.ToggleBackupSettingFragment
com.android.settings.biometrics.fingerprint.FingerprintSettings$FingerprintSettingsFragment
com.android.settings.bluetooth.BluetoothDeviceDetailsFragment
com.android.settings.bluetooth.BluetoothPairingDetail
com.android.settings.bluetooth.DevicePickerFragment
com.android.settings.dashboard.profileselector.ProfileSelectAccountFragment
com.android.settings.dashboard.profileselector.ProfileSelectManageApplications
com.android.settings.dashboard.profileselector.ProfileSelectLocationFragment
com.android.settings.datausage.AppDataUsage
com.android.settings.datausage.DataUsageList
com.android.settings.datausage.DataUsageSummary
com.android.settings.datetime.timezone.TimeZoneSettings
com.android.settings.development.compat.PlatformCompatDashboard
com.android.settings.deviceinfo.PublicVolumeSettings
com.android.settings.deviceinfo.StorageDashboardNoHeaderFragment
com.android.settings.deviceinfo.legal.ModuleLicensesDashboard
com.android.settings.enterprise.ApplicationListFragment$AdminGrantedPermissionCamera
com.android.settings.enterprise.ApplicationListFragment$AdminGrantedPermissionLocation
com.android.settings.enterprise.ApplicationListFragment$AdminGrantedPermissionMicrophone
com.android.settings.enterprise.ApplicationListFragment$EnterpriseInstalledPackages
com.android.settings.enterprise.EnterpriseSetDefaultAppsListFragment
com.android.settings.fuelgauge.AdvancedPowerUsageDetail
com.android.settings.fuelgauge.InactiveApps
com.android.settings.fuelgauge.RestrictedAppDetails
com.android.settings.IccLockSettings
com.android.settings.inputmethod.InputMethodAndSubtypeEnabler
com.android.settings.inputmethod.KeyboardLayoutPickerFragment
com.android.settings.inputmethod.SpellCheckersSettings
com.android.settings.location.LocationPersonalSettings
com.android.settings.location.LocationWorkProfileSettings
com.android.settings.network.apn.ApnEditor
com.android.settings.network.apn.ApnSettings
com.android.settings.network.telephony.NetworkSelectSettings
com.android.settings.notification.app.AppNotificationSettings
com.android.settings.notification.app.ChannelNotificationSettings
com.android.settings.notification.history.NotificationStation
com.android.settings.notification.RedactionInterstitial$RedactionInterstitialFragment
com.android.settings.notification.zen.ZenModeEventRuleSettings
com.android.settings.notification.zen.ZenModeScheduleRuleSettings
com.android.settings.notification.zen.ZenCustomRuleNotificationsSettings
com.android.settings.notification.zen.ZenCustomRuleCallsSettings
com.android.settings.notification.zen.ZenCustomRuleConfigSettings
com.android.settings.notification.zen.ZenCustomRuleSettings
com.android.settings.notification.zen.ZenCustomRuleBlockedEffectsSettings
com.android.settings.notification.zen.ZenCustomRuleMessagesSettings
com.android.settings.password.ChooseLockGeneric$ChooseLockGenericFragment
com.android.settings.password.SetupChooseLockGeneric$InternalActivity$InternalSetupChooseLockGenericFragment
com.android.settings.password.SetupChooseLockGeneric$SetupChooseLockGenericFragment
com.android.settings.print.PrintJobSettingsFragment
com.android.settings.print.PrintServiceSettingsFragment
com.android.settings.SetupRedactionInterstitial$SetupRedactionInterstitialFragment
com.android.settings.shortcut.CreateShortcut
com.android.settings.TestingSettings
com.android.settings.UserCredentialsSettings
com.android.settings.users.AppRestrictionsFragment
com.android.settings.users.RestrictedProfileSettings
com.android.settings.users.UserDetailsSettings
com.android.settings.vpn2.AppManagementFragment
com.android.settings.vpn2.VpnSettings
com.android.settings.wallpaper.WallpaperTypeSettings
com.android.settings.wifi.ChangeWifiStateDetails
com.android.settings.wifi.calling.WifiCallingSettingsForSub
com.android.settings.wifi.details.WifiNetworkDetailsFragment
com.android.settings.wifi.dpp.WifiNetworkListFragment
com.android.settings.wifi.p2p.WifiP2pSettings
com.android.settings.wifi.savedaccesspoints.SavedAccessPointsWifiSettings
com.android.settings.wifi.WifiInfo
com.android.settings.applications.specialaccess.notificationaccess.NotificationAccessDetails
com.android.settings.wifi.details2.WifiNetworkDetailsFragment2
com.android.settings.wifi.savedaccesspoints2.SavedAccessPointsWifiSettings2

View File

@@ -0,0 +1,9 @@
com.android.settings.deletionhelper.ActivationWarningFragment
com.android.settings.applications.appops.AppOpsCategory
com.android.settings.CustomListPreference$CustomListPreferenceDialogFragment
com.android.settings.password.SaveAndFinishWorker
com.android.settings.RestrictedListPreference$RestrictedListPreferenceDialogFragment
com.android.settings.password.ConfirmDeviceCredentialBaseFragment$LastTryDialog
com.android.settings.password.CredentialCheckResultTracker
com.android.settings.dashboard.profileselector.ProfileSelectDialog
com.android.settings.panel.PanelFragment

View File

@@ -0,0 +1 @@
com.android.settings.search.FakeSettingsFragment

View File

@@ -0,0 +1,20 @@
com.android.settings.accessibility.AccessibilitySlicePreferenceController
com.android.settings.accessibility.MagnificationAlwaysOnPreferenceController
com.android.settings.accessibility.MagnificationFollowTypingPreferenceController
com.android.settings.accessibility.MagnificationJoystickPreferenceController
com.android.settings.accessibility.ReduceBrightColorsIntensityPreferenceController
com.android.settings.accessibility.ReduceBrightColorsPersistencePreferenceController
com.android.settings.biometrics.face.FaceSettingsAttentionPreferenceController
com.android.settings.core.TogglePreferenceControllerTest$FakeToggle
com.android.settings.display.DeviceStateAutoRotateSettingController
com.android.settings.display.SmartAutoRotatePreferenceController
com.android.settings.gestures.OneHandedMainSwitchPreferenceController
com.android.settings.network.telephony.AutoDataSwitchPreferenceController
com.android.settings.network.telephony.Enhanced4gBasePreferenceController
com.android.settings.network.telephony.MmsMessagePreferenceController
com.android.settings.notification.RingVolumePreferenceController
com.android.settings.testutils.FakeInvalidSliderController
com.android.settings.testutils.FakeSliderController
com.android.settings.testutils.FakeToggleController
com.android.settings.users.GuestTelephonyPreferenceController
com.android.settings.wifi.details2.WifiAutoConnectPreferenceController2

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,5 @@
sdk=NEWEST_SDK
shadows=\
com.android.settings.testutils.shadow.ShadowThreadUtils \
com.android.settings.network.ShadowServiceManagerExtend
instrumentedPackages=androidx.preference

View File

@@ -0,0 +1,16 @@
#!/bin/bash
# This script detects the presence of new robolectric java tests within
# commits to be uploaded. If a new file is detected the script will print an
# error message and return an error code. Intended to be used as a repo hook.
new_robolectric_tests=$(
git diff --name-status $REPO_LREV | grep "^A.*tests/robotests.*\.java")
if [ $new_robolectric_tests != "" ]
then
echo "New Robolectric unit tests detected. Please submit junit tests" \
"instead, in the tests/junit directory." \
"See go/android-platform-robolectric-cleanup."
echo $new_robolectric_tests
exit 1
fi

View File

@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<com.google.android.setupdesign.GlifLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/setup_wizard_layout"
style="?attr/fingerprint_layout_theme"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:clipToPadding="false"
android:clipChildren="false">
<FrameLayout
android:layout_width="@dimen/fingerprint_find_sensor_graphic_size"
android:layout_height="@dimen/fingerprint_find_sensor_graphic_size"
android:layout_gravity="center_horizontal|bottom">
<ImageView
android:id="@+id/fingerprint_sensor_location_animation"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@string/security_settings_fingerprint_enroll_find_sensor_content_description"
android:src="@drawable/fingerprint_sensor_location"
android:scaleType="centerInside"/>
</FrameLayout>
<LinearLayout
style="@style/SudContentFrame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:clipToPadding="false"
android:clipChildren="false">
<TextView
style="@style/SudDescription.Glif"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/sud_description_glif_margin_top"
android:text="@string/security_settings_fingerprint_enroll_find_sensor_message"/>
<View
android:layout_height="0dp"
android:layout_width="match_parent"
android:layout_weight="1"/>
<Button
style="@style/SudGlifButton.Secondary"
android:id="@+id/next_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:layout_gravity="end"
android:text="@string/fingerprint_enroll_button_next" />
</LinearLayout>
</FrameLayout>
</com.google.android.setupdesign.GlifLayout>

View File

@@ -0,0 +1,134 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2021 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.
-->
<com.google.android.setupdesign.GlifLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/setup_wizard_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:theme="@style/GlifV3Theme.Footer"
android:orientation="vertical"
android:icon="@drawable/ic_delete_accent"
app:sucHeaderText="@string/main_clear_title">
<ScrollView
android:id="@+id/main_clear_scrollview"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/main_clear_container"
style="@style/SudContentFrame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
style="@style/TextAppearance.SudGlifItemSummary"
android:id="@+id/sud_layout_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/main_clear_desc"/>
<TextView
android:id="@+id/also_erases_external"
style="@style/TextAppearance.SudGlifItemSummary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:text="@string/main_clear_desc_also_erases_external"/>
<TextView
android:id="@+id/also_erases_esim"
style="@style/TextAppearance.SudGlifItemSummary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:text="@string/main_clear_desc_also_erases_esim"/>
<TextView
android:id="@+id/accounts_label"
style="@style/TextAppearance.SudGlifItemSummary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:text="@string/main_clear_accounts"/>
<LinearLayout
android:id="@+id/accounts"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone">
<!-- Do not add any children here as they will be removed in the MainClear.java
code. A list of accounts will be inserted programmatically. -->
</LinearLayout>
<TextView
android:id="@+id/other_users_present"
style="@style/TextAppearance.SudGlifItemSummary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:text="@string/main_clear_other_users_present"/>
<TextView
android:id="@+id/no_cancel_mobile_plan"
style="@style/TextAppearance.SudGlifItemSummary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:text="@string/main_clear_desc_no_cancel_mobile_plan"/>
<TextView
android:id="@+id/erase_external_option_text"
style="@style/TextAppearance.SudGlifItemSummary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/main_clear_desc_erase_external_storage"/>
<LinearLayout
android:id="@+id/erase_external_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:focusable="true"
android:clickable="true">
<CheckBox
android:id="@+id/erase_external"
style="@style/SudCheckBox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:focusable="false"
android:clickable="false"
android:duplicateParentState="true"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:orientation="vertical">
<TextView
style="@style/TextAppearance.SudGlifItemTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/erase_external_storage"/>
<TextView
style="@style/TextAppearance.SudGlifItemSummary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/erase_external_storage_description"/>
</LinearLayout>
</LinearLayout>
<include layout="@layout/reset_esim_checkbox"/>
</LinearLayout>
</ScrollView>
</com.google.android.setupdesign.GlifLayout>

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2021 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.
-->
<com.google.android.setupdesign.GlifLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:theme="@style/SudThemeGlifV3.DayNight"
android:orientation="vertical"
android:id="@+id/setup_wizard_layout"
android:icon="@drawable/ic_delete_accent"
app:sucHeaderText="@string/main_clear_confirm_title">
<LinearLayout
style="@style/SudContentFrame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/sud_layout_description"
style="@style/SudItemTitle.GlifDescription"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/main_clear_final_desc"/>
</LinearLayout>
</com.google.android.setupdesign.GlifLayout>

View File

@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- A test preference layout containing all required widgets -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="@android:id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@android:id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Subhead"
android:textColor="?android:attr/textColorPrimary"
android:ellipsize="marquee"
android:fadingEdge="horizontal" />
<TextView
android:id="@android:id/summary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@android:id/title"
android:layout_alignStart="@android:id/title"
android:visibility="gone"
android:textAlignment="viewStart"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body1"
android:textColor="?android:attr/textColorSecondary"
android:maxLines="10" />
<LinearLayout
android:id="@android:id/widget_frame"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:minWidth="58dip"
android:gravity="end|center_vertical"
android:orientation="vertical" />
</LinearLayout>

View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- Since Robolectric can't inflate @*android:layout/alert_dialog_button_bar_material in
../res/layout/wifi_add_network_view.xml, so this Layout overrides button bar part. -->
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<include
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
layout="@layout/wifi_dialog"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true">
<Button
android:id="@android:id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button
android:id="@android:id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
</RelativeLayout>

Binary file not shown.

View File

@@ -0,0 +1,20 @@
<!--
Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<!-- List of packages that should be allowlisted for slice uri access. Do not translate -->
<string-array name="slice_allowlist_package_names" translatable="false"/>
</resources>

View File

@@ -0,0 +1,103 @@
<!--
Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<bool name="config_enableColorTemperature">false</bool>
<bool name="config_show_camera_laser_sensor">false</bool>
<bool name="config_show_connectivity_monitor">false</bool>
<bool name="config_additional_system_update_setting_enable">true</bool>
<bool name="config_show_wifi_settings">false</bool>
<bool name="config_show_toggle_airplane">false</bool>
<bool name="config_show_private_dns_settings">false</bool>
<bool name="config_show_app_info_settings_memory">true</bool>
<bool name="config_show_app_info_settings_battery">false</bool>
<bool name="config_show_high_power_apps">false</bool>
<bool name="config_show_alarm_volume">false</bool>
<bool name="config_show_charging_sounds">false</bool>
<bool name="config_show_media_volume">false</bool>
<bool name="config_show_notification_ringtone">false</bool>
<bool name="config_show_notification_volume">false</bool>
<bool name="config_show_screen_locking_sounds">false</bool>
<bool name="config_show_touch_sounds">false</bool>
<bool name="config_show_encryption_and_credentials_encryption_status">false</bool>
<bool name="config_show_premium_sms">false</bool>
<bool name="config_show_data_saver">false</bool>
<bool name="config_show_enabled_vr_listeners">false</bool>
<bool name="config_location_mode_available">false</bool>
<bool name="config_show_location_scanning">false</bool>
<bool name="config_show_location_services">false</bool>
<bool name="config_show_manage_device_admin">false</bool>
<bool name="config_show_unlock_set_or_change">false</bool>
<bool name="config_show_screen_pinning_settings">false</bool>
<bool name="config_show_manage_trust_agents">false</bool>
<bool name="config_show_show_password">false</bool>
<bool name="config_show_trust_agent_click_intent">false</bool>
<bool name="config_show_wallpaper_attribution">false</bool>
<bool name="config_show_assist_and_voice_input">false</bool>
<bool name="config_show_phone_language">false</bool>
<bool name="config_show_virtual_keyboard_pref">false</bool>
<bool name="config_show_physical_keyboard_pref">false</bool>
<bool name="config_show_tts_settings_summary">false</bool>
<bool name="config_show_pointer_speed">false</bool>
<bool name="config_show_vibrate_input_devices">false</bool>
<bool name="config_show_reset_dashboard">false</bool>
<bool name="config_show_device_model">false</bool>
<bool name="config_show_top_level_accessibility">false</bool>
<bool name="config_show_top_level_battery">false</bool>
<bool name="config_show_top_level_connected_devices">false</bool>
<bool name="config_show_top_level_display">false</bool>
<bool name="config_show_wifi_ip_address">false</bool>
<bool name="config_show_wifi_mac_address">false</bool>
<bool name="config_disable_uninstall_update">true</bool>
<bool name="config_show_device_name">false</bool>
<bool name="config_use_legacy_suggestion">false</bool>
<bool name="config_show_avatar_in_homepage">true</bool>
<bool name="config_show_branded_account_in_device_info">false</bool>
<bool name="config_show_emergency_info_in_device_info">false</bool>
<bool name="config_show_smooth_display">false</bool>
<bool name="config_settingsintelligence_slice_supported">true</bool>
<!-- Whether or not extra preview panels should be used for screen zoom setting. -->
<bool name="config_enable_extra_screen_zoom_preview">false</bool>
<!-- Whether dismissal timestamp should be kept before deletion -->
<bool name="config_keep_contextual_card_dismissal_timestamp">true</bool>
<!-- List of a11y components on the device allowed to be enabled by Settings Slices -->
<string-array name="config_settings_slices_accessibility_components" translatable="false">
<item>fake_package/fake_service</item>
</string-array>
<!-- List of packages that should be allowlisted for slice uri access. Do not translate -->
<string-array name="slice_allowlist_package_names" translatable="false">
<item>com.android.settings.slice_allowlist_package</item>
</string-array>
<!-- Email address for the homepage contextual cards feedback -->
<string name="config_contextual_card_feedback_email" translatable="false">test@test.test</string>
<!-- Grayscale settings intent -->
<string name="config_grayscale_settings_intent" translatable="false">intent:#Intent;action=test.test;end</string>
<!-- List containing the injected tile keys which are suppressed. -->
<string-array name="config_suppress_injected_tile_keys" translatable="false">
<item>injected_tile_key</item>
<item>injected_tile_key2</item>
</string-array>
<!-- Uri to query non-public Slice Uris. -->
<string name="config_non_public_slice_query_uri" translatable="false">content://com.android.settings.slices/test</string>
</resources>

View File

@@ -0,0 +1,24 @@
<!--
Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<!-- Whether or not new device intro suggestion is supported for this device -->
<bool name="config_enableColorTemperature">true</bool>
<bool name="config_show_camera_laser_sensor">true</bool>
<bool name="config_show_connectivity_monitor">true</bool>
<bool name="config_show_smooth_display">true</bool>
<bool name="config_show_location_services">true</bool>
</resources>

Some files were not shown because too many files have changed in this diff Show More