Merge "Dump the DynamicDenylistManager stored data into bugreport" into main
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 75c6fbb..26386c5 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -3389,16 +3389,6 @@
<action android:name="android.settings.USER_SETTINGS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
- <intent-filter>
- <action android:name="com.android.settings.action.SETTINGS" />
- </intent-filter>
- <meta-data android:name="com.android.settings.order" android:value="-45"/>
- <meta-data android:name="com.android.settings.category"
- android:value="com.android.settings.category.ia.system" />
- <meta-data android:name="com.android.settings.summary_uri"
- android:value="content://com.android.settings.dashboard.SummaryProvider/user" />
- <meta-data android:name="com.android.settings.icon"
- android:resource="@drawable/ic_settings_multiuser" />
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
android:value="com.android.settings.users.UserSettings" />
<meta-data android:name="com.android.settings.HIGHLIGHT_MENU_KEY"
diff --git a/res/xml/system_dashboard_fragment.xml b/res/xml/system_dashboard_fragment.xml
index 1f5559f..54dc195 100644
--- a/res/xml/system_dashboard_fragment.xml
+++ b/res/xml/system_dashboard_fragment.xml
@@ -93,6 +93,14 @@
</Preference>
<Preference
+ android:key="system_multiuser"
+ android:title="@string/user_settings_title"
+ android:icon="@drawable/ic_settings_multiuser"
+ android:order="-45"
+ android:fragment="com.android.settings.users.UserSettings"
+ settings:controller="com.android.settings.users.MultiUserPreferenceController"/>
+
+ <Preference
android:key="reset_dashboard"
android:title="@string/reset_dashboard_title"
android:icon="@drawable/ic_restore"
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index 4c20231..3b0dd40 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -781,12 +781,6 @@
Utils.isBandwidthControlEnabled(), isAdmin)
|| somethingChanged;
- somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
- Settings.UserSettingsActivity.class.getName()),
- UserHandle.MU_ENABLED && UserManager.supportsMultipleUsers()
- && !Utils.isMonkeyRunning(), isAdmin)
- || somethingChanged;
-
final boolean showDev = DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(this)
&& !Utils.isMonkeyRunning();
somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index eeca139..f7e341b 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -1209,7 +1209,9 @@
for (UserHandle userHandle : profiles) {
UserProperties userProperties = userManager.getUserProperties(userHandle);
if (userProperties.getShowInSettings() == UserProperties.SHOW_IN_SETTINGS_SEPARATE) {
- if (Flags.allowPrivateProfile() && userProperties.getHideInSettingsInQuietMode()) {
+ if (Flags.allowPrivateProfile()
+ && userProperties.getShowInQuietMode()
+ == UserProperties.SHOW_IN_QUIET_MODE_HIDDEN) {
if (!userManager.isQuietModeEnabled(userHandle)) {
return true;
} else {
diff --git a/src/com/android/settings/accounts/AccountPreferenceController.java b/src/com/android/settings/accounts/AccountPreferenceController.java
index b2cdb77..3e6feb7 100644
--- a/src/com/android/settings/accounts/AccountPreferenceController.java
+++ b/src/com/android/settings/accounts/AccountPreferenceController.java
@@ -42,6 +42,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
+import android.content.pm.UserProperties;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
@@ -312,7 +313,8 @@
&& !(Flags.allowPrivateProfile() && profile.isPrivateProfile())
&& (mType & ProfileSelectFragment.ProfileType.PERSONAL) != 0))
&& !(mUm.getUserProperties(profile.getUserHandle())
- .getHideInSettingsInQuietMode() && profile.isQuietModeEnabled())) {
+ .getShowInQuietMode() == UserProperties.SHOW_IN_QUIET_MODE_HIDDEN
+ && profile.isQuietModeEnabled())) {
updateProfileUi(profile);
}
}
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java
index 1855667..7a2f26f 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java
@@ -160,6 +160,7 @@
+ ", reason = "
+ reason);
mBluetoothDeviceUpdater.forceUpdate();
+ AudioSharingUtils.updateActiveDeviceIfNeeded(mLocalBtManager);
}
@Override
@@ -204,6 +205,7 @@
+ ", reason = "
+ reason);
mBluetoothDeviceUpdater.forceUpdate();
+ AudioSharingUtils.updateActiveDeviceIfNeeded(mLocalBtManager);
}
@Override
@@ -299,7 +301,7 @@
mPreferenceGroup.setVisible(false);
mAudioSharingSettingsPreference.setVisible(false);
- if (isAvailable() && mBluetoothDeviceUpdater != null) {
+ if (isAvailable()) {
mBluetoothDeviceUpdater.setPrefContext(screen.getContext());
mBluetoothDeviceUpdater.forceUpdate();
}
@@ -309,6 +311,7 @@
public int getAvailabilityStatus() {
return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)
&& Flags.enableLeAudioSharing()
+ && mBluetoothDeviceUpdater != null
? AVAILABLE_UNSEARCHABLE
: UNSUPPORTED_ON_DEVICE;
}
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java
index 58ff322..be02519 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java
@@ -192,6 +192,7 @@
+ sourceId
+ ", reason = "
+ reason);
+ AudioSharingUtils.updateActiveDeviceIfNeeded(mBtManager);
}
@Override
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java
index 53e095b..9210074 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java
@@ -16,6 +16,7 @@
package com.android.settings.connecteddevice.audiosharing;
+import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothCsipSetCoordinator;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
@@ -89,11 +90,11 @@
* @return A list of ordered connected devices eligible for the audio sharing. The active device
* is placed in the first place if it exists.
*/
- public static ArrayList<CachedBluetoothDevice> buildOrderedConnectedLeadDevices(
+ public static List<CachedBluetoothDevice> buildOrderedConnectedLeadDevices(
LocalBluetoothManager localBtManager,
Map<Integer, List<CachedBluetoothDevice>> groupedConnectedDevices,
boolean filterByInSharing) {
- ArrayList<CachedBluetoothDevice> orderedDevices = new ArrayList<>();
+ List<CachedBluetoothDevice> orderedDevices = new ArrayList<>();
LocalBluetoothLeBroadcastAssistant assistant =
localBtManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
if (assistant == null) return orderedDevices;
@@ -234,4 +235,32 @@
ThreadUtils.postOnMainThread(
() -> Toast.makeText(context, message, Toast.LENGTH_LONG).show());
}
+
+ /** Automatically update active device if needed. */
+ public static void updateActiveDeviceIfNeeded(LocalBluetoothManager localBtManager) {
+ if (localBtManager == null) return;
+ Map<Integer, List<CachedBluetoothDevice>> groupedConnectedDevices =
+ fetchConnectedDevicesByGroupId(localBtManager);
+ List<CachedBluetoothDevice> devicesInSharing =
+ buildOrderedConnectedLeadDevices(
+ localBtManager, groupedConnectedDevices, /* filterByInSharing= */ true);
+ if (devicesInSharing.isEmpty()) return;
+ List<BluetoothDevice> devices =
+ BluetoothAdapter.getDefaultAdapter().getMostRecentlyConnectedDevices();
+ CachedBluetoothDevice targetDevice = null;
+ int targetDeviceIdx = -1;
+ for (CachedBluetoothDevice device : devicesInSharing) {
+ if (devices.contains(device.getDevice())) {
+ int idx = devices.indexOf(device.getDevice());
+ if (idx > targetDeviceIdx) {
+ targetDeviceIdx = idx;
+ targetDevice = device;
+ }
+ }
+ }
+ if (targetDevice != null && !isActiveLeAudioDevice(targetDevice)) {
+ Log.d(TAG, "Set active device: " + targetDevice.getDevice().getAnonymizedAddress());
+ targetDevice.setActive();
+ }
+ }
}
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsActiveDeviceController.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsActiveDeviceController.java
index f1119fc..d001409 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsActiveDeviceController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsActiveDeviceController.java
@@ -25,10 +25,10 @@
import androidx.preference.PreferenceScreen;
import com.android.settings.core.BasePreferenceController;
-import com.android.settings.widget.SummaryUpdater;
public class AudioStreamsActiveDeviceController extends BasePreferenceController
- implements SummaryUpdater.OnSummaryChangeListener, DefaultLifecycleObserver {
+ implements AudioStreamsActiveDeviceSummaryUpdater.OnSummaryChangeListener,
+ DefaultLifecycleObserver {
public static final String KEY = "audio_streams_active_device";
private final AudioStreamsActiveDeviceSummaryUpdater mSummaryHelper;
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsActiveDeviceSummaryUpdater.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsActiveDeviceSummaryUpdater.java
index 66101f7..cf79596 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsActiveDeviceSummaryUpdater.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsActiveDeviceSummaryUpdater.java
@@ -19,28 +19,30 @@
import android.annotation.Nullable;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
+import android.text.TextUtils;
import android.util.Log;
import com.android.settings.bluetooth.Utils;
import com.android.settings.connecteddevice.audiosharing.AudioSharingUtils;
-import com.android.settings.widget.SummaryUpdater;
import com.android.settingslib.bluetooth.BluetoothCallback;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.utils.ThreadUtils;
import java.util.Optional;
-public class AudioStreamsActiveDeviceSummaryUpdater extends SummaryUpdater
- implements BluetoothCallback {
- private static final String TAG = "AudioStreamsListenWithSummaryUpdater";
+public class AudioStreamsActiveDeviceSummaryUpdater implements BluetoothCallback {
+ private static final String TAG = "AudioStreamsActiveDeviceSummaryUpdater";
private static final boolean DEBUG = BluetoothUtils.D;
private final LocalBluetoothManager mBluetoothManager;
+ private String mSummary;
+ private OnSummaryChangeListener mListener;
public AudioStreamsActiveDeviceSummaryUpdater(
Context context, OnSummaryChangeListener listener) {
- super(context, listener);
mBluetoothManager = Utils.getLocalBluetoothManager(context);
+ mListener = listener;
}
@Override
@@ -59,8 +61,7 @@
}
}
- @Override
- public void register(boolean register) {
+ void register(boolean register) {
if (register) {
notifyChangeIfNeeded();
mBluetoothManager.getEventManager().registerCallback(this);
@@ -69,8 +70,18 @@
}
}
- @Override
- protected String getSummary() {
+ private void notifyChangeIfNeeded() {
+ ThreadUtils.postOnBackgroundThread(
+ () -> {
+ String summary = getSummary();
+ if (!TextUtils.equals(mSummary, summary)) {
+ mSummary = summary;
+ ThreadUtils.postOnMainThread(() -> mListener.onSummaryChanged(summary));
+ }
+ });
+ }
+
+ private String getSummary() {
var activeSink = getActiveSinkOnAssistant(mBluetoothManager);
if (activeSink.isEmpty()) {
return "No active LE Audio device";
@@ -95,4 +106,14 @@
}
return Optional.empty();
}
+
+ /** Interface definition for a callback to be invoked when the summary has been changed. */
+ interface OnSummaryChangeListener {
+ /**
+ * Called when summary has changed.
+ *
+ * @param summary The new summary.
+ */
+ void onSummaryChanged(String summary);
+ }
}
diff --git a/src/com/android/settings/dashboard/SummaryProvider.java b/src/com/android/settings/dashboard/SummaryProvider.java
index 6acc663..cd388b9 100644
--- a/src/com/android/settings/dashboard/SummaryProvider.java
+++ b/src/com/android/settings/dashboard/SummaryProvider.java
@@ -20,15 +20,10 @@
import android.content.ContentProvider;
import android.content.ContentValues;
-import android.content.Context;
-import android.content.pm.UserInfo;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
-import android.os.UserHandle;
-import android.os.UserManager;
-import com.android.settings.R;
import com.android.settings.backup.BackupSettingsHelper;
/** Provide preference summary for injected items. */
@@ -44,14 +39,6 @@
bundle.putString(META_DATA_PREFERENCE_SUMMARY,
new BackupSettingsHelper(getContext()).getSummary());
break;
- case USER:
- final Context context = getContext();
- final UserInfo info = context.getSystemService(UserManager.class).getUserInfo(
- UserHandle.myUserId());
- bundle.putString(META_DATA_PREFERENCE_SUMMARY,
- context.getString(R.string.users_summary,
- info.name));
- break;
default:
throw new IllegalArgumentException("Unknown Uri format: " + uri);
}
diff --git a/src/com/android/settings/dashboard/profileselector/ProfileSelectDialog.java b/src/com/android/settings/dashboard/profileselector/ProfileSelectDialog.java
index 561a51a..eaad7f3 100644
--- a/src/com/android/settings/dashboard/profileselector/ProfileSelectDialog.java
+++ b/src/com/android/settings/dashboard/profileselector/ProfileSelectDialog.java
@@ -235,7 +235,7 @@
private static boolean shouldHideUserInQuietMode(
UserHandle userHandle, UserManager userManager) {
UserProperties userProperties = userManager.getUserProperties(userHandle);
- return userProperties.getHideInSettingsInQuietMode()
+ return userProperties.getShowInQuietMode() == UserProperties.SHOW_IN_QUIET_MODE_HIDDEN
&& userManager.isQuietModeEnabled(userHandle);
}
}
diff --git a/src/com/android/settings/inputmethod/SpellCheckerPreference.java b/src/com/android/settings/inputmethod/SpellCheckerPreference.java
index 8c8942a..b8028fe 100644
--- a/src/com/android/settings/inputmethod/SpellCheckerPreference.java
+++ b/src/com/android/settings/inputmethod/SpellCheckerPreference.java
@@ -25,6 +25,7 @@
import android.view.View.OnClickListener;
import android.view.textservice.SpellCheckerInfo;
+import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog.Builder;
import androidx.preference.PreferenceViewHolder;
@@ -41,11 +42,15 @@
class SpellCheckerPreference extends CustomListPreference {
private final SpellCheckerInfo[] mScis;
- private Intent mIntent;
+ @VisibleForTesting
+ Intent mIntent;
public SpellCheckerPreference(final Context context, final SpellCheckerInfo[] scis) {
super(context, null);
mScis = scis;
+ setLayoutResource(
+ com.android.settingslib.widget.preference.twotarget.R.layout.preference_two_target);
+
setWidgetLayoutResource(R.layout.preference_widget_gear);
if (scis == null) {
return;
@@ -108,14 +113,26 @@
@Override
public void onBindViewHolder(PreferenceViewHolder view) {
super.onBindViewHolder(view);
+ final View divider = view.findViewById(
+ com.android.settingslib.widget.preference.twotarget.R.id.two_target_divider);
+ final View widgetFrame = view.findViewById(android.R.id.widget_frame);
+ if (divider != null) {
+ divider.setVisibility(mIntent != null ? View.VISIBLE : View.GONE);
+ }
+ if (widgetFrame != null) {
+ widgetFrame.setVisibility(mIntent != null ? View.VISIBLE : View.GONE);
+ }
+
View settingsButton = view.findViewById(R.id.settings_button);
- settingsButton.setVisibility(mIntent != null ? View.VISIBLE : View.INVISIBLE);
- settingsButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- onSettingsButtonClicked();
- }
- });
+ if (settingsButton != null) {
+ settingsButton.setVisibility(mIntent != null ? View.VISIBLE : View.INVISIBLE);
+ settingsButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ onSettingsButtonClicked();
+ }
+ });
+ }
}
private void onSettingsButtonClicked() {
diff --git a/src/com/android/settings/users/MultiUserPreferenceController.java b/src/com/android/settings/users/MultiUserPreferenceController.java
new file mode 100644
index 0000000..2eb57ec
--- /dev/null
+++ b/src/com/android/settings/users/MultiUserPreferenceController.java
@@ -0,0 +1,46 @@
+/*
+ * 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.users;
+
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.core.BasePreferenceController;
+
+public class MultiUserPreferenceController extends BasePreferenceController {
+
+ public MultiUserPreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return (UserHandle.MU_ENABLED && UserManager.supportsMultipleUsers()
+ && !Utils.isMonkeyRunning()) ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ UserManager um = mContext.getSystemService(UserManager.class);
+ UserInfo info = um.getUserInfo(UserHandle.myUserId());
+ return mContext.getString(R.string.users_summary, info.name);
+ }
+}
diff --git a/src/com/android/settings/wifi/dpp/WifiDppConfiguratorAuthActivity.java b/src/com/android/settings/wifi/dpp/WifiDppConfiguratorAuthActivity.java
index aa77dc1..010607f 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppConfiguratorAuthActivity.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppConfiguratorAuthActivity.java
@@ -37,6 +37,8 @@
* Sharing a Wi-Fi network by QR code after unlocking. Used by {@code InternetDialog} in QS.
*/
public class WifiDppConfiguratorAuthActivity extends InstrumentedActivity {
+ private static final String WIFI_SHARING_KEY_ALIAS = "wifi_sharing_auth_key";
+ private static final int MAX_UNLOCK_SECONDS = 60;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -46,7 +48,9 @@
Intent authIntent = getSystemService(KeyguardManager.class)
.createConfirmDeviceCredentialIntent(
getText(R.string.wifi_dpp_lockscreen_title), null, getUserId());
- if (authIntent == null) {
+ if (authIntent == null
+ || WifiDppUtils.isUnlockedWithinSeconds(
+ WIFI_SHARING_KEY_ALIAS, MAX_UNLOCK_SECONDS)) {
startQrCodeActivity();
finish();
} else {
diff --git a/src/com/android/settings/wifi/dpp/WifiDppUtils.java b/src/com/android/settings/wifi/dpp/WifiDppUtils.java
index 83a1571..23a6a54 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppUtils.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppUtils.java
@@ -30,6 +30,8 @@
import android.os.UserHandle;
import android.os.VibrationEffect;
import android.os.Vibrator;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
import android.text.TextUtils;
import com.android.settings.R;
@@ -37,9 +39,19 @@
import com.android.settingslib.wifi.AccessPoint;
import com.android.wifitrackerlib.WifiEntry;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
import java.time.Duration;
import java.util.List;
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.KeyGenerator;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+
/**
* Here are the items shared by both WifiDppConfiguratorActivity & WifiDppEnrolleeActivity
*
@@ -97,6 +109,8 @@
private static final Duration VIBRATE_DURATION_QR_CODE_RECOGNITION = Duration.ofMillis(3);
+ private static final String AES_CBC_PKCS7_PADDING = "AES/CBC/PKCS7Padding";
+
/**
* Returns whether the device support WiFi DPP.
*/
@@ -367,6 +381,48 @@
}
/**
+ * Checks whether the device is unlocked recently.
+ *
+ * @param keyStoreAlias key
+ * @param seconds how many seconds since the device is unlocked
+ * @return whether the device is unlocked within the time
+ */
+ public static boolean isUnlockedWithinSeconds(String keyStoreAlias, int seconds) {
+ try {
+ Cipher cipher = Cipher.getInstance(AES_CBC_PKCS7_PADDING);
+ cipher.init(Cipher.ENCRYPT_MODE, generateSecretKey(keyStoreAlias, seconds));
+ cipher.doFinal();
+ return true;
+ } catch (NoSuchPaddingException
+ | IllegalBlockSizeException
+ | NoSuchAlgorithmException
+ | BadPaddingException
+ | InvalidKeyException e) {
+ return false;
+ }
+ }
+
+ private static SecretKey generateSecretKey(String keyStoreAlias, int seconds) {
+ KeyGenParameterSpec spec = new KeyGenParameterSpec
+ .Builder(keyStoreAlias, KeyProperties.PURPOSE_ENCRYPT)
+ .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
+ .setUserAuthenticationRequired(true)
+ .setUserAuthenticationParameters(
+ seconds,
+ KeyProperties.AUTH_DEVICE_CREDENTIAL | KeyProperties.AUTH_BIOMETRIC_STRONG)
+ .build();
+ try {
+ KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES);
+ keyGenerator.init(spec);
+ return keyGenerator.generateKey();
+ } catch (NoSuchAlgorithmException
+ | InvalidAlgorithmParameterException e) {
+ return null;
+ }
+ }
+
+ /**
* Shows authentication screen to confirm credentials (pin, pattern or password) for the current
* user of the device.
*
diff --git a/tests/robotests/src/com/android/settings/inputmethod/SpellCheckerPreferenceTest.java b/tests/robotests/src/com/android/settings/inputmethod/SpellCheckerPreferenceTest.java
new file mode 100644
index 0000000..d8e54b0
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/inputmethod/SpellCheckerPreferenceTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.inputmethod;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.content.Intent;
+import android.view.View;
+import android.view.textservice.SpellCheckerInfo;
+
+import androidx.preference.PreferenceViewHolder;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class SpellCheckerPreferenceTest {
+
+ private Context mContext;
+ private PreferenceViewHolder mViewHolder;
+ private View mDivider;
+ private SpellCheckerPreference mPreference;
+ private final SpellCheckerInfo[] mScis = new SpellCheckerInfo[]{};
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ mPreference = new SpellCheckerPreference(mContext, mScis);
+ }
+
+ @Test
+ public void onBindViewHolder_withIntent_DividerIsVisible() {
+ final View view = spy(View.inflate(mContext, mPreference.getLayoutResource(), null));
+ mViewHolder = PreferenceViewHolder.createInstanceForTests(view);
+ mDivider = view.findViewById(
+ com.android.settingslib.widget.preference.twotarget.R.id.two_target_divider);
+ mPreference.mIntent = new Intent(Intent.ACTION_MAIN);
+
+ mPreference.onBindViewHolder(mViewHolder);
+
+ assertThat(mDivider.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void onBindViewHolder_withoutIntent_DividerIsNotExist() {
+ final View view = spy(View.inflate(mContext, mPreference.getLayoutResource(), null));
+ mViewHolder = PreferenceViewHolder.createInstanceForTests(view);
+ mDivider = view.findViewById(
+ com.android.settingslib.widget.preference.twotarget.R.id.two_target_divider);
+
+ mPreference.onBindViewHolder(mViewHolder);
+
+ assertThat(mDivider.getVisibility()).isEqualTo(View.GONE);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/users/MultiUserPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/users/MultiUserPreferenceControllerTest.java
new file mode 100644
index 0000000..fd73eab
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/users/MultiUserPreferenceControllerTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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.users;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.testutils.shadow.ShadowUserManager;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowUserManager.class})
+public class MultiUserPreferenceControllerTest {
+
+ private Context mContext;
+ private ShadowUserManager mUserManager;
+ private MultiUserPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+ mUserManager = ShadowUserManager.getShadow();
+ mController = new MultiUserPreferenceController(mContext, "test_key");
+ }
+
+ @After
+ public void tearDown() {
+ ShadowUserManager.reset();
+ }
+
+ @Test
+ public void getAvailabilityStatus_multiuserEnabled_shouldReturnAvailable() {
+ mUserManager.setSupportsMultipleUsers(true);
+
+ assertThat(mController.getAvailabilityStatus())
+ .isEqualTo(BasePreferenceController.AVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_multiuserDisabled_shouldReturnNotAvailable() {
+ mUserManager.setSupportsMultipleUsers(false);
+
+ assertThat(mController.getAvailabilityStatus())
+ .isEqualTo(BasePreferenceController.UNSUPPORTED_ON_DEVICE);
+ }
+}