diff --git a/Android.bp b/Android.bp
index 9ddadaf00b1..f980bfa6581 100644
--- a/Android.bp
+++ b/Android.bp
@@ -90,8 +90,11 @@ android_library {
"WifiTrackerLib",
"SettingsLibActivityEmbedding",
"Settings-change-ids",
+ "androidx.room_room-runtime",
],
+ plugins: ["androidx.room_room-compiler-plugin"],
+
libs: [
"telephony-common",
"ims-common",
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index f5da15fe711..935c1a47f0d 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -6,6 +6,11 @@
+
+
+
@@ -2961,6 +2966,13 @@
android:value="@string/menu_key_battery"/>
+
+
"ማሳወቂያዎችን ድረስ"
- "ካሜራ"
- "ኦዲዮ ቅረጽ"
- - "ድምጽ አጫውት"
+ - "ድምፅ አጫውት"
- "ቅንጥብ መለያ አንብብ"
- "ቅንጥብ መለያ ቀይር"
- "የሚዲያ አዝራሮች"
- "የድምጽ ትኩረት"
- - "ዋናው ድምጽ መቆጣጠሪያ"
+ - "ዋናው ድምፅ መቆጣጠሪያ"
- "የድምጽ መጠን"
- - "የጥሪ ድምጽ መጠን"
+ - "የጥሪ ድምፅ መጠን"
- "የማህደረ መረጃ መጠን"
- "የማንቂያ ድምፅ መጠን"
- - "የማሳወቂያ ድምጽ መጠን"
+ - "የማሳወቂያ ድምፅ መጠን"
- "የብሉቱዝ ድምፅ መጠን"
- "እንደነቃ አቆይ"
- "አካባቢን ይቆጣጠሩ"
@@ -250,7 +250,7 @@
- "ማሳወቂያዎችን ይድረሱ"
- "ካሜራ"
- "ኦዲዮ ቅረጽ"
- - "ድምጽ አጫውት"
+ - "ድምፅ አጫውት"
- "ቅንጥብ ሰሌዳ አንብብ"
- "ቅንጥብ መለያ ቀይር"
- "የሚዲያ አዝራሮች"
@@ -260,7 +260,7 @@
- "የጥሪ መጠን"
- "የማህደረ መረጃ ክፍልፍል"
- "የማንቂያ ድምፅ መጠን"
- - "የማሳወቂያ ድምጽ መጠን"
+ - "የማሳወቂያ ድምፅ መጠን"
- "የብሉቱዝ ድምፅ መጠን"
- "እንደነቃ አቆይ"
- "አካባቢ"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index c66c5b984eb..645a8264b98 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -2599,6 +2599,8 @@
Screen saver
screensaver
+
+ Unavailable because bedtime mode is on
Use screen saver
@@ -8022,6 +8024,8 @@
Change settings for each app that sends notifications
Apps shown on device
This app doesn\u2019t support enhanced settings
+ More settings
+ More settings are available inside this app
VR helper services
diff --git a/res/xml/notification_access_permission_details.xml b/res/xml/notification_access_permission_details.xml
index 9867b6d4c0a..32a79e8ee36 100644
--- a/res/xml/notification_access_permission_details.xml
+++ b/res/xml/notification_access_permission_details.xml
@@ -64,6 +64,12 @@
settings:searchable="false"
settings:controller="com.android.settings.applications.specialaccess.notificationaccess.BridgedAppsLinkPreferenceController" />
+
+
+ android:summary="@string/summary_placeholder"/>
+ android:order="999"/>
diff --git a/src/com/android/settings/applications/specialaccess/notificationaccess/MoreSettingsPreferenceController.java b/src/com/android/settings/applications/specialaccess/notificationaccess/MoreSettingsPreferenceController.java
new file mode 100644
index 00000000000..a1ae3a4eef9
--- /dev/null
+++ b/src/com/android/settings/applications/specialaccess/notificationaccess/MoreSettingsPreferenceController.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.applications.specialaccess.notificationaccess;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.service.notification.NotificationListenerService;
+
+import androidx.preference.Preference;
+
+import com.android.settings.core.BasePreferenceController;
+
+import java.util.List;
+
+/**
+ * Controls link to reach more preference settings inside the app.
+ */
+public class MoreSettingsPreferenceController extends BasePreferenceController {
+
+ private static final String TAG = "MoreSettingsPrefContr";
+ private static final String KEY_MORE_SETTINGS = "more_settings";
+
+ PackageManager mPm;
+ String mPackage;
+ int mUserId;
+ Intent mIntent = new Intent(Intent.ACTION_MAIN)
+ .addCategory(NotificationListenerService.INTENT_CATEGORY_SETTINGS_HOME);
+
+ public MoreSettingsPreferenceController(Context context) {
+ super(context, KEY_MORE_SETTINGS);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ final List resolveInfos = mPm.queryIntentActivities(
+ mIntent,
+ PackageManager.ResolveInfoFlags.of(0));
+ if (resolveInfos == null || resolveInfos.isEmpty()) {
+ return CONDITIONALLY_UNAVAILABLE;
+ }
+ return AVAILABLE;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_MORE_SETTINGS;
+ }
+
+ public MoreSettingsPreferenceController setPackageManager(PackageManager pm) {
+ mPm = pm;
+ return this;
+ }
+
+ public MoreSettingsPreferenceController setPackage(String pkg) {
+ mPackage = pkg;
+ mIntent.setPackage(mPackage);
+ return this;
+ }
+
+ public void updateState(Preference preference) {
+ preference.setIntent(mIntent);
+ }
+}
diff --git a/src/com/android/settings/applications/specialaccess/notificationaccess/NotificationAccessDetails.java b/src/com/android/settings/applications/specialaccess/notificationaccess/NotificationAccessDetails.java
index e6feebb92ab..531fb228dcd 100644
--- a/src/com/android/settings/applications/specialaccess/notificationaccess/NotificationAccessDetails.java
+++ b/src/com/android/settings/applications/specialaccess/notificationaccess/NotificationAccessDetails.java
@@ -124,6 +124,9 @@ public class NotificationAccessDetails extends DashboardFragment {
.setCn(mComponentName)
.setUserId(mUserId)
.setTargetSdk(listenerTargetSdk);
+ use(MoreSettingsPreferenceController.class)
+ .setPackage(mComponentName.getPackageName())
+ .setPackageManager(mPm);
final int finalListenerTargetSdk = listenerTargetSdk;
getPreferenceControllers().forEach(controllers -> {
controllers.forEach(controller -> {
diff --git a/src/com/android/settings/display/ScreenSaverPreferenceController.java b/src/com/android/settings/display/ScreenSaverPreferenceController.java
index 676a567f202..de1aaea0bd8 100644
--- a/src/com/android/settings/display/ScreenSaverPreferenceController.java
+++ b/src/com/android/settings/display/ScreenSaverPreferenceController.java
@@ -18,6 +18,7 @@ import android.os.UserManager;
import androidx.preference.Preference;
+import com.android.settings.R;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.dream.DreamSettings;
import com.android.settingslib.core.AbstractPreferenceController;
@@ -26,9 +27,12 @@ public class ScreenSaverPreferenceController extends AbstractPreferenceControlle
PreferenceControllerMixin {
private static final String KEY_SCREEN_SAVER = "screensaver";
+ private final boolean mDreamsDisabledByAmbientModeSuppression;
public ScreenSaverPreferenceController(Context context) {
super(context);
+ mDreamsDisabledByAmbientModeSuppression = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_dreamsDisabledByAmbientModeSuppressionConfig);
}
@Override
@@ -47,7 +51,12 @@ public class ScreenSaverPreferenceController extends AbstractPreferenceControlle
@Override
public void updateState(Preference preference) {
- preference.setSummary(DreamSettings.getSummaryTextWithDreamName(mContext));
+ if (mDreamsDisabledByAmbientModeSuppression
+ && AmbientDisplayAlwaysOnPreferenceController.isAodSuppressedByBedtime(mContext)) {
+ preference.setSummary(R.string.screensaver_settings_when_to_dream_bedtime);
+ } else {
+ preference.setSummary(DreamSettings.getSummaryTextWithDreamName(mContext));
+ }
}
private boolean isSystemUser() {
diff --git a/src/com/android/settings/dream/WhenToDreamPreferenceController.java b/src/com/android/settings/dream/WhenToDreamPreferenceController.java
index 4108e850538..02ae6a75f39 100644
--- a/src/com/android/settings/dream/WhenToDreamPreferenceController.java
+++ b/src/com/android/settings/dream/WhenToDreamPreferenceController.java
@@ -20,7 +20,10 @@ import android.content.Context;
import androidx.preference.Preference;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.R;
import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.display.AmbientDisplayAlwaysOnPreferenceController;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.dream.DreamBackend;
@@ -29,19 +32,34 @@ public class WhenToDreamPreferenceController extends AbstractPreferenceControlle
private static final String WHEN_TO_START = "when_to_start";
private final DreamBackend mBackend;
+ private final boolean mDreamsDisabledByAmbientModeSuppression;
WhenToDreamPreferenceController(Context context) {
+ this(context, context.getResources().getBoolean(
+ com.android.internal.R.bool.config_dreamsDisabledByAmbientModeSuppressionConfig));
+ }
+
+ @VisibleForTesting
+ WhenToDreamPreferenceController(Context context,
+ boolean dreamsDisabledByAmbientModeSuppression) {
super(context);
mBackend = DreamBackend.getInstance(context);
+ mDreamsDisabledByAmbientModeSuppression = dreamsDisabledByAmbientModeSuppression;
}
@Override
public void updateState(Preference preference) {
super.updateState(preference);
- int resId = DreamSettings.getDreamSettingDescriptionResId(mBackend.getWhenToDreamSetting());
- preference.setSummary(preference.getContext().getString(resId));
+ if (mDreamsDisabledByAmbientModeSuppression
+ && AmbientDisplayAlwaysOnPreferenceController.isAodSuppressedByBedtime(mContext)) {
+ preference.setSummary(R.string.screensaver_settings_when_to_dream_bedtime);
+ } else {
+ final int resId = DreamSettings.getDreamSettingDescriptionResId(
+ mBackend.getWhenToDreamSetting());
+ preference.setSummary(resId);
+ }
}
@Override
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageContentProvider.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageContentProvider.java
new file mode 100644
index 00000000000..8d5ba33f8ca
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageContentProvider.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.fuelgauge.batteryusage;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.UriMatcher;
+import android.database.Cursor;
+import android.net.Uri;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.settings.fuelgauge.batteryusage.db.BatteryState;
+import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDao;
+import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDatabase;
+
+import java.time.Clock;
+import java.time.Duration;
+
+/** {@link ContentProvider} class to fetch battery usage data. */
+public class BatteryUsageContentProvider extends ContentProvider {
+ private static final String TAG = "BatteryUsageContentProvider";
+
+ // TODO: Updates the duration to a more reasonable value for since-last-full-charge.
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ public static final Duration QUERY_DURATION_HOURS = Duration.ofHours(28);
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ public static final String QUERY_KEY_TIMESTAMP = "timestamp";
+
+ /** Codes */
+ private static final int BATTERY_STATE_CODE = 1;
+ private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
+
+ static {
+ sUriMatcher.addURI(
+ DatabaseUtils.AUTHORITY,
+ /*path=*/ DatabaseUtils.BATTERY_STATE_TABLE,
+ /*code=*/ BATTERY_STATE_CODE);
+ }
+
+ private Clock mClock;
+ private BatteryStateDao mBatteryStateDao;
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ public void setClock(Clock clock) {
+ this.mClock = clock;
+ }
+
+ @Override
+ public boolean onCreate() {
+ if (DatabaseUtils.isWorkProfile(getContext())) {
+ Log.w(TAG, "do not create provider for work profile");
+ return false;
+ }
+ mClock = Clock.systemUTC();
+ mBatteryStateDao = BatteryStateDatabase.getInstance(getContext()).batteryStateDao();
+ Log.w(TAG, "create content provider from " + getCallingPackage());
+ return true;
+ }
+
+ @Nullable
+ @Override
+ public Cursor query(
+ @NonNull Uri uri,
+ @Nullable String[] strings,
+ @Nullable String s,
+ @Nullable String[] strings1,
+ @Nullable String s1) {
+ switch (sUriMatcher.match(uri)) {
+ case BATTERY_STATE_CODE:
+ return getBatteryStates(uri);
+ default:
+ throw new IllegalArgumentException("unknown URI: " + uri);
+ }
+ }
+
+ @Nullable
+ @Override
+ public String getType(@NonNull Uri uri) {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public Uri insert(@NonNull Uri uri, @Nullable ContentValues contentValues) {
+ switch (sUriMatcher.match(uri)) {
+ case BATTERY_STATE_CODE:
+ try {
+ mBatteryStateDao.insert(BatteryState.create(contentValues));
+ return uri;
+ } catch (RuntimeException e) {
+ Log.e(TAG, "insert() from:" + uri + " error:" + e);
+ return null;
+ }
+ default:
+ throw new IllegalArgumentException("unknown URI: " + uri);
+ }
+ }
+
+ @Override
+ public int delete(@NonNull Uri uri, @Nullable String s, @Nullable String[] strings) {
+ throw new UnsupportedOperationException("unsupported!");
+ }
+
+ @Override
+ public int update(
+ @NonNull Uri uri,
+ @Nullable ContentValues contentValues,
+ @Nullable String s,
+ @Nullable String[] strings) {
+ throw new UnsupportedOperationException("unsupported!");
+ }
+
+ private Cursor getBatteryStates(Uri uri) {
+ final long defaultTimestamp = mClock.millis() - QUERY_DURATION_HOURS.toMillis();
+ final long queryTimestamp = getQueryTimestamp(uri, defaultTimestamp);
+ return getBatteryStates(uri, queryTimestamp);
+ }
+
+ private Cursor getBatteryStates(Uri uri, long firstTimestamp) {
+ final long timestamp = mClock.millis();
+ Cursor cursor = null;
+ try {
+ cursor = mBatteryStateDao.getCursorAfter(firstTimestamp);
+ } catch (RuntimeException e) {
+ Log.e(TAG, "query() from:" + uri + " error:" + e);
+ }
+ // TODO: Invokes hourly job recheck.
+ Log.w(TAG, "query battery states in " + (mClock.millis() - timestamp) + "/ms");
+ return cursor;
+ }
+
+ // If URI contains query parameter QUERY_KEY_TIMESTAMP, use the value directly.
+ // Otherwise, load the data for QUERY_DURATION_HOURS by default.
+ private long getQueryTimestamp(Uri uri, long defaultTimestamp) {
+ final String firstTimestampString = uri.getQueryParameter(QUERY_KEY_TIMESTAMP);
+ if (TextUtils.isEmpty(firstTimestampString)) {
+ Log.w(TAG, "empty query timestamp");
+ return defaultTimestamp;
+ }
+
+ try {
+ return Long.parseLong(firstTimestampString);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "invalid query timestamp: " + firstTimestampString, e);
+ return defaultTimestamp;
+ }
+ }
+}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java b/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
new file mode 100644
index 00000000000..4ad1ac3df53
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.fuelgauge.batteryusage;
+
+import android.content.Context;
+import android.os.UserManager;
+
+/** A utility class to operate battery usage database. */
+public final class DatabaseUtils {
+ private static final String TAG = "DatabaseUtils";
+
+ /** An authority name of the battery content provider. */
+ public static final String AUTHORITY = "com.android.settings.battery.usage.provider";
+ /** A table name for battery usage history. */
+ public static final String BATTERY_STATE_TABLE = "BatteryState";
+
+ /** Returns true if current user is a work profile user. */
+ public static boolean isWorkProfile(Context context) {
+ final UserManager userManager = context.getSystemService(UserManager.class);
+ return userManager.isManagedProfile() && !userManager.isSystemUser();
+ }
+}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryState.java b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryState.java
new file mode 100644
index 00000000000..11db1185398
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryState.java
@@ -0,0 +1,397 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.fuelgauge.batteryusage.db;
+
+import android.content.ContentValues;
+import android.content.Intent;
+import android.os.BatteryManager;
+
+import androidx.room.Entity;
+import androidx.room.PrimaryKey;
+
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+
+import java.text.SimpleDateFormat;
+import java.time.Duration;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+/** A {@link Entity} class to save battery states snapshot into database. */
+@Entity
+public class BatteryState {
+ private static String sCacheZoneId;
+ private static SimpleDateFormat sCacheSimpleDateFormat;
+
+ @PrimaryKey(autoGenerate = true)
+ private long mId;
+
+ // Records the app relative information.
+ public final long uid;
+ public final long userId;
+ public final String appLabel;
+ public final String packageName;
+ // Whether the data is represented as system component or not?
+ public final boolean isHidden;
+ // Records the timestamp relative information.
+ public final long bootTimestamp;
+ public final long timestamp;
+ public final String zoneId;
+ // Records the battery usage relative information.
+ public final double totalPower;
+ public final double consumePower;
+ public final double percentOfTotal;
+ public final long foregroundUsageTimeInMs;
+ public final long backgroundUsageTimeInMs;
+ public final int drainType;
+ public final int consumerType;
+ // Records the battery intent relative information.
+ public final int batteryLevel;
+ public final int batteryStatus;
+ public final int batteryHealth;
+
+ public BatteryState(
+ long uid,
+ long userId,
+ String appLabel,
+ String packageName,
+ boolean isHidden,
+ long bootTimestamp,
+ long timestamp,
+ String zoneId,
+ double totalPower,
+ double consumePower,
+ double percentOfTotal,
+ long foregroundUsageTimeInMs,
+ long backgroundUsageTimeInMs,
+ int drainType,
+ int consumerType,
+ int batteryLevel,
+ int batteryStatus,
+ int batteryHealth) {
+ // Records the app relative information.
+ this.uid = uid;
+ this.userId = userId;
+ this.appLabel = appLabel;
+ this.packageName = packageName;
+ this.isHidden = isHidden;
+ // Records the timestamp relative information.
+ this.bootTimestamp = bootTimestamp;
+ this.timestamp = timestamp;
+ this.zoneId = zoneId;
+ // Records the battery usage relative information.
+ this.totalPower = totalPower;
+ this.consumePower = consumePower;
+ this.percentOfTotal = percentOfTotal;
+ this.foregroundUsageTimeInMs = foregroundUsageTimeInMs;
+ this.backgroundUsageTimeInMs = backgroundUsageTimeInMs;
+ this.drainType = drainType;
+ this.consumerType = consumerType;
+ // Records the battery intent relative information.
+ this.batteryLevel = batteryLevel;
+ this.batteryStatus = batteryStatus;
+ this.batteryHealth = batteryHealth;
+ }
+
+ /** Sets the auto-generated content ID. */
+ public void setId(long id) {
+ this.mId = id;
+ }
+
+ /** Gets the auto-generated content ID. */
+ public long getId() {
+ return mId;
+ }
+
+ @Override
+ @SuppressWarnings("JavaUtilDate")
+ public String toString() {
+ final String currentZoneId = TimeZone.getDefault().getID();
+ if (!currentZoneId.equals(sCacheZoneId) || sCacheSimpleDateFormat == null) {
+ sCacheZoneId = currentZoneId;
+ sCacheSimpleDateFormat = new SimpleDateFormat("MMM dd,yyyy HH:mm:ss", Locale.US);
+ }
+ final String recordAtDateTime = sCacheSimpleDateFormat.format(new Date(timestamp));
+ final StringBuilder builder = new StringBuilder()
+ .append("\nBatteryState{")
+ .append(String.format(Locale.US,
+ "\n\tpackage=%s|label=%s|uid=%d|userId=%d|isHidden=%b",
+ packageName, appLabel, uid, userId, isHidden))
+ .append(String.format(Locale.US, "\n\ttimestamp=%s|zoneId=%s|bootTimestamp=%d",
+ recordAtDateTime, zoneId, Duration.ofMillis(bootTimestamp).getSeconds()))
+ .append(String.format(Locale.US,
+ "\n\tusage=%f|total=%f|consume=%f|elapsedTime=%d|%d",
+ percentOfTotal, totalPower, consumePower,
+ Duration.ofMillis(foregroundUsageTimeInMs).getSeconds(),
+ Duration.ofMillis(backgroundUsageTimeInMs).getSeconds()))
+ .append(String.format(Locale.US,
+ "\n\tdrain=%d|consumer=%d", drainType, consumerType))
+ .append(String.format(Locale.US, "\n\tbattery=%d|status=%d|health=%d\n}",
+ batteryLevel, batteryStatus, batteryHealth));
+ return builder.toString();
+ }
+
+
+ /** Creates new {@link BatteryState} from {@link ContentValues}. */
+ public static BatteryState create(ContentValues contentValues) {
+ Builder builder = BatteryState.newBuilder();
+ if (contentValues.containsKey("uid")) {
+ builder.setUid(contentValues.getAsLong("uid"));
+ }
+ if (contentValues.containsKey("userId")) {
+ builder.setUserId(contentValues.getAsLong("userId"));
+ }
+ if (contentValues.containsKey("appLabel")) {
+ builder.setAppLabel(contentValues.getAsString("appLabel"));
+ }
+ if (contentValues.containsKey("packageName")) {
+ builder.setPackageName(contentValues.getAsString("packageName"));
+ }
+ if (contentValues.containsKey("isHidden")) {
+ builder.setIsHidden(contentValues.getAsBoolean("isHidden"));
+ }
+ if (contentValues.containsKey("bootTimestamp")) {
+ builder.setBootTimestamp(contentValues.getAsLong("bootTimestamp"));
+ }
+ if (contentValues.containsKey("timestamp")) {
+ builder.setTimestamp(contentValues.getAsLong("timestamp"));
+ }
+ if (contentValues.containsKey("consumePower")) {
+ builder.setConsumePower(contentValues.getAsDouble("consumePower"));
+ }
+ if (contentValues.containsKey("totalPower")) {
+ builder.setTotalPower(contentValues.getAsDouble("totalPower"));
+ }
+ if (contentValues.containsKey("percentOfTotal")) {
+ builder.setPercentOfTotal(contentValues.getAsDouble("percentOfTotal"));
+ }
+ if (contentValues.containsKey("foregroundUsageTimeInMs")) {
+ builder.setForegroundUsageTimeInMs(
+ contentValues.getAsLong("foregroundUsageTimeInMs"));
+ }
+ if (contentValues.containsKey("backgroundUsageTimeInMs")) {
+ builder.setBackgroundUsageTimeInMs(
+ contentValues.getAsLong("backgroundUsageTimeInMs"));
+ }
+ if (contentValues.containsKey("drainType")) {
+ builder.setDrainType(contentValues.getAsInteger("drainType"));
+ }
+ if (contentValues.containsKey("consumerType")) {
+ builder.setConsumerType(contentValues.getAsInteger("consumerType"));
+ }
+ if (contentValues.containsKey("batteryLevel")) {
+ builder.setBatteryLevel(contentValues.getAsInteger("batteryLevel"));
+ }
+ if (contentValues.containsKey("batteryStatus")) {
+ builder.setBatteryStatus(contentValues.getAsInteger("batteryStatus"));
+ }
+ if (contentValues.containsKey("batteryHealth")) {
+ builder.setBatteryHealth(contentValues.getAsInteger("batteryHealth"));
+ }
+ return builder.build();
+ }
+
+ /** Creates a new {@link Builder} instance. */
+ public static Builder newBuilder() {
+ return new Builder();
+ }
+
+ /** A convenience builder class to improve readability. */
+ public static class Builder {
+ private long mUid;
+ private long mUserId;
+ private String mAppLabel;
+ private String mPackageName;
+ private boolean mIsHidden;
+ private long mBootTimestamp;
+ private long mTimestamp;
+ private double mTotalPower;
+ private double mConsumePower;
+ private double mPercentOfTotal;
+ private long mForegroundUsageTimeInMs;
+ private long mBackgroundUsageTimeInMs;
+ private int mDrainType;
+ private int mConsumerType;
+ private int mBatteryLevel;
+ private int mBatteryStatus;
+ private int mBatteryHealth;
+
+ /** Sets the uid. */
+ @CanIgnoreReturnValue
+ public Builder setUid(long uid) {
+ this.mUid = uid;
+ return this;
+ }
+
+ /** Sets the user ID. */
+ @CanIgnoreReturnValue
+ public Builder setUserId(long userId) {
+ this.mUserId = userId;
+ return this;
+ }
+
+ /** Sets the app label. */
+ @CanIgnoreReturnValue
+ public Builder setAppLabel(String appLabel) {
+ this.mAppLabel = appLabel;
+ return this;
+ }
+
+ /** Sets the package name. */
+ @CanIgnoreReturnValue
+ public Builder setPackageName(String packageName) {
+ this.mPackageName = packageName;
+ return this;
+ }
+
+ /** Sets the is hidden value. */
+ @CanIgnoreReturnValue
+ public Builder setIsHidden(boolean isHidden) {
+ this.mIsHidden = isHidden;
+ return this;
+ }
+
+ /** Sets the boot timestamp. */
+ @CanIgnoreReturnValue
+ public Builder setBootTimestamp(long bootTimestamp) {
+ this.mBootTimestamp = bootTimestamp;
+ return this;
+ }
+
+ /** Sets the timestamp. */
+ @CanIgnoreReturnValue
+ public Builder setTimestamp(long timestamp) {
+ this.mTimestamp = timestamp;
+ return this;
+ }
+
+ /** Sets the total power. */
+ @CanIgnoreReturnValue
+ public Builder setTotalPower(double totalPower) {
+ this.mTotalPower = totalPower;
+ return this;
+ }
+
+ /** Sets the consumed power. */
+ @CanIgnoreReturnValue
+ public Builder setConsumePower(double consumePower) {
+ this.mConsumePower = consumePower;
+ return this;
+ }
+
+ /** Sets the percentage of total. */
+ @CanIgnoreReturnValue
+ public Builder setPercentOfTotal(double percentOfTotal) {
+ this.mPercentOfTotal = percentOfTotal;
+ return this;
+ }
+
+ /** Sets the foreground usage time. */
+ @CanIgnoreReturnValue
+ public Builder setForegroundUsageTimeInMs(long foregroundUsageTimeInMs) {
+ this.mForegroundUsageTimeInMs = foregroundUsageTimeInMs;
+ return this;
+ }
+
+ /** Sets the background usage time. */
+ @CanIgnoreReturnValue
+ public Builder setBackgroundUsageTimeInMs(long backgroundUsageTimeInMs) {
+ this.mBackgroundUsageTimeInMs = backgroundUsageTimeInMs;
+ return this;
+ }
+
+ /** Sets the drain type. */
+ @CanIgnoreReturnValue
+ public Builder setDrainType(int drainType) {
+ this.mDrainType = drainType;
+ return this;
+ }
+
+ /** Sets the consumer type. */
+ @CanIgnoreReturnValue
+ public Builder setConsumerType(int consumerType) {
+ this.mConsumerType = consumerType;
+ return this;
+ }
+
+ /** Sets the battery level. */
+ @CanIgnoreReturnValue
+ public Builder setBatteryLevel(int batteryLevel) {
+ this.mBatteryLevel = batteryLevel;
+ return this;
+ }
+
+ /** Sets the battery status. */
+ @CanIgnoreReturnValue
+ public Builder setBatteryStatus(int batteryStatus) {
+ this.mBatteryStatus = batteryStatus;
+ return this;
+ }
+
+ /** Sets the battery health. */
+ @CanIgnoreReturnValue
+ public Builder setBatteryHealth(int batteryHealth) {
+ this.mBatteryHealth = batteryHealth;
+ return this;
+ }
+
+ /** Sets the battery intent. */
+ @CanIgnoreReturnValue
+ public Builder setBatteryIntent(Intent batteryIntent) {
+ final int level = batteryIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
+ final int scale = batteryIntent.getIntExtra(BatteryManager.EXTRA_SCALE, 0);
+ this.mBatteryLevel =
+ scale == 0
+ ? -1 /*invalid battery level*/
+ : Math.round((level / (float) scale) * 100f);
+ this.mBatteryStatus =
+ batteryIntent.getIntExtra(
+ BatteryManager.EXTRA_STATUS,
+ BatteryManager.BATTERY_STATUS_UNKNOWN);
+ this.mBatteryHealth =
+ batteryIntent.getIntExtra(
+ BatteryManager.EXTRA_HEALTH,
+ BatteryManager.BATTERY_HEALTH_UNKNOWN);
+ return this;
+ }
+
+ /** Builds the BatteryState. */
+ public BatteryState build() {
+ return new BatteryState(
+ mUid,
+ mUserId,
+ mAppLabel,
+ mPackageName,
+ mIsHidden,
+ mBootTimestamp,
+ mTimestamp,
+ /*zoneId=*/ TimeZone.getDefault().getID(),
+ mTotalPower,
+ mConsumePower,
+ mPercentOfTotal,
+ mForegroundUsageTimeInMs,
+ mBackgroundUsageTimeInMs,
+ mDrainType,
+ mConsumerType,
+ mBatteryLevel,
+ mBatteryStatus,
+ mBatteryHealth);
+ }
+
+ private Builder() {}
+ }
+}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDao.java b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDao.java
new file mode 100644
index 00000000000..b1afa6b6e9a
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDao.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.fuelgauge.batteryusage.db;
+
+import android.database.Cursor;
+
+import androidx.room.Dao;
+import androidx.room.Insert;
+import androidx.room.OnConflictStrategy;
+import androidx.room.Query;
+
+import java.util.List;
+
+/** Data access object for accessing {@link BatteryState} in the database. */
+@Dao
+public interface BatteryStateDao {
+
+ /** Inserts a {@link BatteryState} data into the database. */
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ void insert(BatteryState state);
+
+ /** Inserts {@link BatteryState} data into the database. */
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ void insertAll(List states);
+
+ /** Lists all recorded data after a specific timestamp. */
+ @Query("SELECT * FROM BatteryState WHERE timestamp > :timestamp ORDER BY timestamp DESC")
+ List getAllAfter(long timestamp);
+
+ /** Gets the {@link Cursor} of all recorded data from a specific timestamp. */
+ @Query("SELECT * FROM BatteryState WHERE timestamp >= :timestamp ORDER BY timestamp DESC")
+ Cursor getCursorAfter(long timestamp);
+
+ /** Get the count of distinct timestamp after a specific timestamp. */
+ @Query("SELECT COUNT(DISTINCT timestamp) FROM BatteryState WHERE timestamp > :timestamp")
+ int getDistinctTimestampCount(long timestamp);
+
+ /** Lists all distinct timestamps after a specific timestamp. */
+ @Query("SELECT DISTINCT timestamp FROM BatteryState WHERE timestamp > :timestamp")
+ List getDistinctTimestamps(long timestamp);
+
+ /** Deletes all recorded data before a specific timestamp. */
+ @Query("DELETE FROM BatteryState WHERE timestamp <= :timestamp")
+ void clearAllBefore(long timestamp);
+
+ /** Clears all recorded data in the database. */
+ @Query("DELETE FROM BatteryState")
+ void clearAll();
+}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDatabase.java b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDatabase.java
new file mode 100644
index 00000000000..939654639a0
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDatabase.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.fuelgauge.batteryusage.db;
+
+import android.content.Context;
+import android.util.Log;
+
+import androidx.room.Database;
+import androidx.room.Room;
+import androidx.room.RoomDatabase;
+
+/** A {@link RoomDatabase} for battery usage states history. */
+@Database(
+ entities = {BatteryState.class},
+ version = 1)
+public abstract class BatteryStateDatabase extends RoomDatabase {
+ private static final String TAG = "BatteryStateDatabase";
+
+ private static BatteryStateDatabase sBatteryStateDatabase;
+
+ /** Provides DAO for battery state table. */
+ public abstract BatteryStateDao batteryStateDao();
+
+ /** Gets or creates an instance of {@link RoomDatabase}. */
+ public static BatteryStateDatabase getInstance(Context context) {
+ if (sBatteryStateDatabase == null) {
+ sBatteryStateDatabase =
+ Room.databaseBuilder(
+ context, BatteryStateDatabase.class, "battery-usage-db-v1")
+ // Allows accessing data in the main thread for dumping bugreport.
+ .allowMainThreadQueries()
+ .fallbackToDestructiveMigration()
+ .build();
+ Log.d(TAG, "initialize battery states database");
+ }
+ return sBatteryStateDatabase;
+ }
+
+ /** Sets the instance of {@link RoomDatabase}. */
+ public static void setBatteryStateDatabase(BatteryStateDatabase database) {
+ BatteryStateDatabase.sBatteryStateDatabase = database;
+ }
+}
diff --git a/src/com/android/settings/network/UiccSlotUtil.java b/src/com/android/settings/network/UiccSlotUtil.java
index 7b8d330834b..4c26995f377 100644
--- a/src/com/android/settings/network/UiccSlotUtil.java
+++ b/src/com/android/settings/network/UiccSlotUtil.java
@@ -28,7 +28,6 @@ import android.telephony.UiccSlotMapping;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.uicc.UiccController;
import com.android.settingslib.utils.ThreadUtils;
import com.google.common.collect.ImmutableList;
@@ -273,6 +272,7 @@ public class UiccSlotUtil {
if (slotId == INVALID_PHYSICAL_SLOT_ID) {
for (int i = 0; i < slots.length; i++) {
if (slots[i].isRemovable()
+ && !slots[i].getIsEuicc()
&& !slots[i].getPorts().stream().findFirst().get().isActive()
&& slots[i].getCardStateInfo() != UiccSlotInfo.CARD_STATE_INFO_ERROR
&& slots[i].getCardStateInfo() != UiccSlotInfo.CARD_STATE_INFO_RESTRICTED) {
@@ -413,4 +413,29 @@ public class UiccSlotUtil {
.findFirst()
.orElse(INVALID_LOGICAL_SLOT_ID);
}
+
+ /**
+ * Return whether the removable psim is enabled.
+ *
+ * @param telMgr is a TelephonyManager.
+ * @return whether the removable psim is enabled.
+ */
+ public static boolean isRemovableSimEnabled(TelephonyManager telMgr) {
+ if (telMgr == null) {
+ return false;
+ }
+ ImmutableList slotInfos = UiccSlotUtil.getSlotInfos(telMgr);
+ boolean isRemovableSimEnabled =
+ slotInfos.stream()
+ .anyMatch(
+ slot -> slot != null
+ && slot.isRemovable()
+ && !slot.getIsEuicc()
+ && slot.getPorts().stream().anyMatch(
+ port -> port.isActive())
+ && slot.getCardStateInfo()
+ == UiccSlotInfo.CARD_STATE_INFO_PRESENT);
+ Log.i(TAG, "isRemovableSimEnabled: " + isRemovableSimEnabled);
+ return isRemovableSimEnabled;
+ }
}
diff --git a/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java b/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java
index a878cb3ebd3..6fa803d0433 100644
--- a/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java
+++ b/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java
@@ -24,7 +24,6 @@ import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.UiccCardInfo;
-import android.telephony.UiccSlotInfo;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
@@ -39,8 +38,6 @@ import com.android.settings.network.SwitchToRemovableSlotSidecar;
import com.android.settings.network.UiccSlotUtil;
import com.android.settings.sim.SimActivationNotifier;
-import com.google.common.collect.ImmutableList;
-
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@@ -586,18 +583,7 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
}
private boolean isRemovableSimEnabled() {
- ImmutableList slotInfos = UiccSlotUtil.getSlotInfos(mTelMgr);
- boolean isRemovableSimEnabled =
- slotInfos.stream()
- .anyMatch(
- slot -> slot != null
- && slot.isRemovable()
- && slot.getPorts().stream().anyMatch(
- port -> port.isActive())
- && slot.getCardStateInfo()
- == UiccSlotInfo.CARD_STATE_INFO_PRESENT);
- Log.i(TAG, "isRemovableSimEnabled: " + isRemovableSimEnabled);
- return isRemovableSimEnabled;
+ return UiccSlotUtil.isRemovableSimEnabled(mTelMgr);
}
private boolean isMultipleEnabledProfilesSupported() {
diff --git a/src/com/android/settings/privacy/PrivacyDashboardFragment.java b/src/com/android/settings/privacy/PrivacyDashboardFragment.java
index 75ed225e3a6..46a05b06109 100644
--- a/src/com/android/settings/privacy/PrivacyDashboardFragment.java
+++ b/src/com/android/settings/privacy/PrivacyDashboardFragment.java
@@ -25,9 +25,12 @@ import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROF
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.SearchIndexableResource;
import com.android.settings.R;
+import com.android.settings.Utils;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.notification.LockScreenNotificationPreferenceController;
import com.android.settings.safetycenter.SafetyCenterManagerWrapper;
@@ -131,5 +134,21 @@ public class PrivacyDashboardFragment extends DashboardFragment {
Context context) {
return buildPreferenceControllers(context, null);
}
+
+ @Override
+ public List getNonIndexableKeys(Context context) {
+ final List keys = super.getNonIndexableKeys(context);
+ final int profileUserId =
+ Utils.getManagedProfileId(
+ UserManager.get(context), UserHandle.myUserId());
+ // If work profile is supported, we should keep the search result.
+ if (profileUserId != UserHandle.USER_NULL) {
+ return keys;
+ }
+
+ // Otherwise, we should hide the search result.
+ keys.add(KEY_NOTIFICATION_WORK_PROFILE_NOTIFICATIONS);
+ return keys;
+ }
};
}
diff --git a/src/com/android/settings/vpn2/ConfigDialog.java b/src/com/android/settings/vpn2/ConfigDialog.java
index bf0dfc9c5b8..be2a68e237a 100644
--- a/src/com/android/settings/vpn2/ConfigDialog.java
+++ b/src/com/android/settings/vpn2/ConfigDialog.java
@@ -626,7 +626,14 @@ class ConfigDialog extends AlertDialog implements TextWatcher,
String proxyPort = mProxyPort.getText().toString().trim();
// 0 is a last resort default, but the interface validates that the proxy port is
// present and non-zero.
- int port = proxyPort.isEmpty() ? 0 : Integer.parseInt(proxyPort);
+ int port = 0;
+ if (!proxyPort.isEmpty()) {
+ try {
+ port = Integer.parseInt(proxyPort);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "Could not parse proxy port integer ", e);
+ }
+ }
profile.proxy = ProxyInfo.buildDirectProxy(proxyHost, port);
} else {
profile.proxy = null;
diff --git a/src/com/android/settings/wifi/NetworkRequestDialogActivity.java b/src/com/android/settings/wifi/NetworkRequestDialogActivity.java
index 30f38d299e5..7b7d33fdce3 100644
--- a/src/com/android/settings/wifi/NetworkRequestDialogActivity.java
+++ b/src/com/android/settings/wifi/NetworkRequestDialogActivity.java
@@ -27,12 +27,14 @@ import android.net.wifi.WifiManager.NetworkRequestUserSelectionCallback;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerExecutor;
+import android.os.Looper;
import android.os.Message;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.FragmentActivity;
+import androidx.fragment.app.FragmentManager;
import com.android.settings.R;
import com.android.settings.wifi.NetworkRequestErrorDialogFragment.ERROR_DIALOG_TYPE;
@@ -143,7 +145,7 @@ public class NetworkRequestDialogActivity extends FragmentActivity implements
super.onPause();
}
- private final Handler mHandler = new Handler() {
+ private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
@@ -162,13 +164,17 @@ public class NetworkRequestDialogActivity extends FragmentActivity implements
dismissDialogs();
// Throws error dialog.
+ final FragmentManager fragmentManager = getSupportFragmentManager();
+ if (fragmentManager.isDestroyed() || fragmentManager.isStateSaved()) {
+ return;
+ }
final NetworkRequestErrorDialogFragment dialogFragment =
NetworkRequestErrorDialogFragment.newInstance();
dialogFragment.setRejectCallback(mUserSelectionCallback);
final Bundle bundle = new Bundle();
bundle.putSerializable(NetworkRequestErrorDialogFragment.DIALOG_TYPE, type);
dialogFragment.setArguments(bundle);
- dialogFragment.show(getSupportFragmentManager(), TAG);
+ dialogFragment.show(fragmentManager, TAG);
mShowingErrorDialog = true;
}
diff --git a/tests/robotests/src/com/android/settings/dream/WhenToDreamPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/dream/WhenToDreamPreferenceControllerTest.java
index 3305cded8ad..458c5c6ceee 100644
--- a/tests/robotests/src/com/android/settings/dream/WhenToDreamPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/dream/WhenToDreamPreferenceControllerTest.java
@@ -16,15 +16,24 @@
package com.android.settings.dream;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.PowerManager;
import androidx.preference.Preference;
import androidx.test.core.app.ApplicationProvider;
+import com.android.settings.R;
+import com.android.settings.display.AmbientDisplayAlwaysOnPreferenceController;
import com.android.settingslib.dream.DreamBackend;
import com.android.settingslib.dream.DreamBackend.WhenToDream;
@@ -38,32 +47,64 @@ import org.robolectric.util.ReflectionHelpers;
@RunWith(RobolectricTestRunner.class)
public class WhenToDreamPreferenceControllerTest {
+ private static final String TEST_PACKAGE = "com.android.test";
private WhenToDreamPreferenceController mController;
private Context mContext;
@Mock
private DreamBackend mBackend;
+ @Mock
+ private PowerManager mPowerManager;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private ApplicationInfo mApplicationInfo;
@Before
- public void setup() {
+ public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
- mContext = ApplicationProvider.getApplicationContext();
- mController = new WhenToDreamPreferenceController(mContext);
+ mContext = spy(ApplicationProvider.getApplicationContext());
+ mController = new WhenToDreamPreferenceController(mContext, true);
ReflectionHelpers.setField(mController, "mBackend", mBackend);
+ when(mContext.getSystemService(PowerManager.class)).thenReturn(mPowerManager);
+ when(mPowerManager.isAmbientDisplaySuppressedForTokenByApp(anyString(), anyInt()))
+ .thenReturn(false);
+
+ mApplicationInfo.uid = 1;
+ when(mContext.getString(
+ com.android.internal.R.string.config_defaultWellbeingPackage)).thenReturn(
+ TEST_PACKAGE);
+
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mPackageManager.getApplicationInfo(TEST_PACKAGE, /* flag= */ 0)).thenReturn(
+ mApplicationInfo);
}
@Test
- public void updateSummary() {
+ public void testUpdateSummary() {
// Don't have to test the other settings because DreamSettings tests that all
// @WhenToDream values map to the correct ResId
final @WhenToDream int testSetting = DreamBackend.WHILE_CHARGING;
final Preference mockPref = mock(Preference.class);
when(mockPref.getContext()).thenReturn(mContext);
when(mBackend.getWhenToDreamSetting()).thenReturn(testSetting);
- final String expectedString =
- mContext.getString(DreamSettings.getDreamSettingDescriptionResId(testSetting));
+ final int expectedResId = DreamSettings.getDreamSettingDescriptionResId(testSetting);
mController.updateState(mockPref);
- verify(mockPref).setSummary(expectedString);
+ verify(mockPref).setSummary(expectedResId);
+ }
+
+ @Test
+ public void testBedtimeModeSuppression() {
+ final Preference mockPref = mock(Preference.class);
+ when(mockPref.getContext()).thenReturn(mContext);
+ when(mBackend.getWhenToDreamSetting()).thenReturn(DreamBackend.WHILE_CHARGING);
+ when(mPowerManager.isAmbientDisplaySuppressedForTokenByApp(anyString(), anyInt()))
+ .thenReturn(true);
+
+ assertTrue(AmbientDisplayAlwaysOnPreferenceController.isAodSuppressedByBedtime(mContext));
+
+ mController.updateState(mockPref);
+ verify(mockPref).setSummary(R.string.screensaver_settings_when_to_dream_bedtime);
}
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageContentProviderTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageContentProviderTest.java
new file mode 100644
index 00000000000..4f145c24bc6
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageContentProviderTest.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.fuelgauge.batteryusage;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.fuelgauge.batteryusage.db.BatteryState;
+import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDatabase;
+import com.android.settings.testutils.BatteryTestUtils;
+import com.android.settings.testutils.FakeClock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.time.Duration;
+import java.util.List;
+
+/** Tests for {@link BatteryUsageContentProvider}. */
+@RunWith(RobolectricTestRunner.class)
+public final class BatteryUsageContentProviderTest {
+ private static final Uri VALID_BATTERY_STATE_CONTENT_URI =
+ new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(DatabaseUtils.AUTHORITY)
+ .appendPath(DatabaseUtils.BATTERY_STATE_TABLE)
+ .build();
+
+ private Context mContext;
+ private BatteryUsageContentProvider mProvider;
+
+ @Before
+ public void setUp() {
+ mContext = ApplicationProvider.getApplicationContext();
+ mProvider = new BatteryUsageContentProvider();
+ mProvider.attachInfo(mContext, /*info=*/ null);
+ BatteryTestUtils.setUpBatteryStateDatabase(mContext);
+ }
+
+ @Test
+ public void onCreate_withoutWorkProfileMode_returnsTrue() {
+ assertThat(mProvider.onCreate()).isTrue();
+ }
+
+ @Test
+ public void onCreate_withWorkProfileMode_returnsFalse() {
+ BatteryTestUtils.setWorkProfile(mContext);
+ assertThat(mProvider.onCreate()).isFalse();
+ }
+
+ @Test
+ public void queryAndInsert_incorrectContentUri_throwsIllegalArgumentException() {
+ final Uri.Builder builder =
+ new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(DatabaseUtils.AUTHORITY)
+ .appendPath(DatabaseUtils.BATTERY_STATE_TABLE + "/0");
+ final Uri uri = builder.build();
+ mProvider.onCreate();
+
+ assertThrows(
+ IllegalArgumentException.class,
+ () ->
+ mProvider.query(
+ uri, /*strings=*/ null, /*s=*/ null, /*strings1=*/ null,
+ /*s1=*/ null));
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> mProvider.insert(uri, /*contentValues=*/ null));
+ }
+
+ @Test
+ public void queryAndInsert_incorrectAuthority_throwsIllegalArgumentException() {
+ final Uri.Builder builder =
+ new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(DatabaseUtils.AUTHORITY + ".debug")
+ .appendPath(DatabaseUtils.BATTERY_STATE_TABLE);
+ final Uri uri = builder.build();
+ mProvider.onCreate();
+
+ assertThrows(
+ IllegalArgumentException.class,
+ () ->
+ mProvider.query(
+ uri, /*strings=*/ null, /*s=*/ null, /*strings1=*/ null,
+ /*s1=*/ null));
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> mProvider.insert(uri, /*contentValues=*/ null));
+ }
+
+ @Test
+ public void query_batteryState_returnsExpectedResult() throws Exception {
+ mProvider.onCreate();
+ final Duration currentTime = Duration.ofHours(52);
+ final long expiredTimeCutoff = currentTime.toMillis()
+ - BatteryUsageContentProvider.QUERY_DURATION_HOURS.toMillis();
+ testQueryBatteryState(currentTime, expiredTimeCutoff, /*hasQueryTimestamp=*/ false);
+ }
+
+ @Test
+ public void query_batteryStateTimestamp_returnsExpectedResult() throws Exception {
+ mProvider.onCreate();
+ final Duration currentTime = Duration.ofHours(52);
+ final long expiredTimeCutoff = currentTime.toMillis() - Duration.ofHours(10).toMillis();
+ testQueryBatteryState(currentTime, expiredTimeCutoff, /*hasQueryTimestamp=*/ true);
+ }
+
+ @Test
+ public void query_incorrectParameterFormat_returnsExpectedResult() throws Exception {
+ mProvider.onCreate();
+ final Duration currentTime = Duration.ofHours(52);
+ final long expiredTimeCutoff =
+ currentTime.toMillis()
+ - BatteryUsageContentProvider.QUERY_DURATION_HOURS.toMillis();
+ testQueryBatteryState(
+ currentTime,
+ expiredTimeCutoff,
+ /*hasQueryTimestamp=*/ false,
+ /*customParameter=*/ "invalid number format");
+ }
+
+ @Test
+ public void insert_batteryState_returnsExpectedResult() {
+ mProvider.onCreate();
+ ContentValues values = new ContentValues();
+ values.put("uid", Long.valueOf(101L));
+ values.put("userId", Long.valueOf(1001L));
+ values.put("appLabel", new String("Settings"));
+ values.put("packageName", new String("com.android.settings"));
+ values.put("timestamp", Long.valueOf(2100021L));
+ values.put("isHidden", Boolean.valueOf(true));
+ values.put("totalPower", Double.valueOf(99.0));
+ values.put("consumePower", Double.valueOf(9.0));
+ values.put("percentOfTotal", Double.valueOf(0.9));
+ values.put("foregroundUsageTimeInMs", Long.valueOf(1000));
+ values.put("backgroundUsageTimeInMs", Long.valueOf(2000));
+ values.put("drainType", Integer.valueOf(1));
+ values.put("consumerType", Integer.valueOf(2));
+ values.put("batteryLevel", Integer.valueOf(51));
+ values.put("batteryStatus", Integer.valueOf(2));
+ values.put("batteryHealth", Integer.valueOf(3));
+
+ final Uri uri = mProvider.insert(VALID_BATTERY_STATE_CONTENT_URI, values);
+
+ assertThat(uri).isEqualTo(VALID_BATTERY_STATE_CONTENT_URI);
+ // Verifies the BatteryState content.
+ final List states =
+ BatteryStateDatabase.getInstance(mContext).batteryStateDao().getAllAfter(0);
+ assertThat(states).hasSize(1);
+ assertThat(states.get(0).uid).isEqualTo(101L);
+ assertThat(states.get(0).userId).isEqualTo(1001L);
+ assertThat(states.get(0).appLabel).isEqualTo("Settings");
+ assertThat(states.get(0).packageName).isEqualTo("com.android.settings");
+ assertThat(states.get(0).isHidden).isTrue();
+ assertThat(states.get(0).timestamp).isEqualTo(2100021L);
+ assertThat(states.get(0).totalPower).isEqualTo(99.0);
+ assertThat(states.get(0).consumePower).isEqualTo(9.0);
+ assertThat(states.get(0).percentOfTotal).isEqualTo(0.9);
+ assertThat(states.get(0).foregroundUsageTimeInMs).isEqualTo(1000);
+ assertThat(states.get(0).backgroundUsageTimeInMs).isEqualTo(2000);
+ assertThat(states.get(0).drainType).isEqualTo(1);
+ assertThat(states.get(0).consumerType).isEqualTo(2);
+ assertThat(states.get(0).batteryLevel).isEqualTo(51);
+ assertThat(states.get(0).batteryStatus).isEqualTo(2);
+ assertThat(states.get(0).batteryHealth).isEqualTo(3);
+ }
+
+ @Test
+ public void insert_partialFieldsContentValues_returnsExpectedResult() {
+ mProvider.onCreate();
+ final ContentValues values = new ContentValues();
+ values.put("packageName", new String("fake_data"));
+ values.put("timestamp", Long.valueOf(2100022L));
+ values.put("batteryLevel", Integer.valueOf(52));
+ values.put("batteryStatus", Integer.valueOf(3));
+ values.put("batteryHealth", Integer.valueOf(2));
+
+ final Uri uri = mProvider.insert(VALID_BATTERY_STATE_CONTENT_URI, values);
+
+ assertThat(uri).isEqualTo(VALID_BATTERY_STATE_CONTENT_URI);
+ // Verifies the BatteryState content.
+ final List states =
+ BatteryStateDatabase.getInstance(mContext).batteryStateDao().getAllAfter(0);
+ assertThat(states).hasSize(1);
+ assertThat(states.get(0).packageName).isEqualTo("fake_data");
+ assertThat(states.get(0).timestamp).isEqualTo(2100022L);
+ assertThat(states.get(0).batteryLevel).isEqualTo(52);
+ assertThat(states.get(0).batteryStatus).isEqualTo(3);
+ assertThat(states.get(0).batteryHealth).isEqualTo(2);
+ }
+
+ @Test
+ public void delete_throwsUnsupportedOperationException() {
+ assertThrows(
+ UnsupportedOperationException.class,
+ () -> mProvider.delete(/*uri=*/ null, /*s=*/ null, /*strings=*/ null));
+ }
+
+ @Test
+ public void update_throwsUnsupportedOperationException() {
+ assertThrows(
+ UnsupportedOperationException.class,
+ () ->
+ mProvider.update(
+ /*uri=*/ null, /*contentValues=*/ null, /*s=*/ null,
+ /*strings=*/ null));
+ }
+
+ private void testQueryBatteryState(
+ Duration currentTime, long expiredTimeCutoff, boolean hasQueryTimestamp)
+ throws Exception {
+ testQueryBatteryState(currentTime, expiredTimeCutoff, hasQueryTimestamp, null);
+ }
+
+ private void testQueryBatteryState(
+ Duration currentTime,
+ long expiredTimeCutoff,
+ boolean hasQueryTimestamp,
+ String customParameter)
+ throws Exception {
+ mProvider.onCreate();
+ final FakeClock fakeClock = new FakeClock();
+ fakeClock.setCurrentTime(currentTime);
+ mProvider.setClock(fakeClock);
+ // Inserts some expired testing data.
+ BatteryTestUtils.insertDataToBatteryStateDatabase(
+ mContext, expiredTimeCutoff - 1, "com.android.sysui1");
+ BatteryTestUtils.insertDataToBatteryStateDatabase(
+ mContext, expiredTimeCutoff - 2, "com.android.sysui2");
+ BatteryTestUtils.insertDataToBatteryStateDatabase(
+ mContext, expiredTimeCutoff - 3, "com.android.sysui3");
+ // Inserts some valid testing data.
+ final String packageName1 = "com.android.settings1";
+ final String packageName2 = "com.android.settings2";
+ final String packageName3 = "com.android.settings3";
+ BatteryTestUtils.insertDataToBatteryStateDatabase(
+ mContext, currentTime.toMillis(), packageName1);
+ BatteryTestUtils.insertDataToBatteryStateDatabase(
+ mContext, expiredTimeCutoff + 2, packageName2);
+ BatteryTestUtils.insertDataToBatteryStateDatabase(
+ mContext, expiredTimeCutoff, packageName3);
+
+ final Uri.Builder builder =
+ new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(DatabaseUtils.AUTHORITY)
+ .appendPath(DatabaseUtils.BATTERY_STATE_TABLE);
+ if (customParameter != null) {
+ builder.appendQueryParameter(
+ BatteryUsageContentProvider.QUERY_KEY_TIMESTAMP, customParameter);
+ } else if (hasQueryTimestamp) {
+ builder.appendQueryParameter(
+ BatteryUsageContentProvider.QUERY_KEY_TIMESTAMP,
+ Long.toString(expiredTimeCutoff));
+ }
+ final Uri batteryStateQueryContentUri = builder.build();
+
+ final Cursor cursor =
+ mProvider.query(
+ batteryStateQueryContentUri,
+ /*strings=*/ null,
+ /*s=*/ null,
+ /*strings1=*/ null,
+ /*s1=*/ null);
+
+ // Verifies the result not include expired data.
+ assertThat(cursor.getCount()).isEqualTo(3);
+ final int packageNameIndex = cursor.getColumnIndex("packageName");
+ // Verifies the first data package name.
+ cursor.moveToFirst();
+ final String actualPackageName1 = cursor.getString(packageNameIndex);
+ assertThat(actualPackageName1).isEqualTo(packageName1);
+ // Verifies the second data package name.
+ cursor.moveToNext();
+ final String actualPackageName2 = cursor.getString(packageNameIndex);
+ assertThat(actualPackageName2).isEqualTo(packageName2);
+ // Verifies the third data package name.
+ cursor.moveToNext();
+ final String actualPackageName3 = cursor.getString(packageNameIndex);
+ assertThat(actualPackageName3).isEqualTo(packageName3);
+ cursor.close();
+ // TODO: add verification for recheck broadcast.
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDaoTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDaoTest.java
new file mode 100644
index 00000000000..41e3f4df1c5
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDaoTest.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.fuelgauge.batteryusage.db;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.database.Cursor;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.testutils.BatteryTestUtils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.List;
+
+/** Tests for {@link BatteryStateDao}. */
+@RunWith(RobolectricTestRunner.class)
+public final class BatteryStateDaoTest {
+ private static final int CURSOR_COLUMN_SIZE = 19;
+ private static final long TIMESTAMP1 = System.currentTimeMillis();
+ private static final long TIMESTAMP2 = System.currentTimeMillis() + 2;
+ private static final long TIMESTAMP3 = System.currentTimeMillis() + 4;
+ private static final String PACKAGE_NAME1 = "com.android.apps.settings";
+ private static final String PACKAGE_NAME2 = "com.android.apps.calendar";
+ private static final String PACKAGE_NAME3 = "com.android.apps.gmail";
+
+ private Context mContext;
+ private BatteryStateDatabase mDatabase;
+ private BatteryStateDao mBatteryStateDao;
+
+ @Before
+ public void setUp() {
+ mContext = ApplicationProvider.getApplicationContext();
+ mDatabase = BatteryTestUtils.setUpBatteryStateDatabase(mContext);
+ mBatteryStateDao = mDatabase.batteryStateDao();
+ BatteryTestUtils.insertDataToBatteryStateDatabase(mContext, TIMESTAMP3, PACKAGE_NAME3);
+ BatteryTestUtils.insertDataToBatteryStateDatabase(mContext, TIMESTAMP2, PACKAGE_NAME2);
+ BatteryTestUtils.insertDataToBatteryStateDatabase(
+ mContext, TIMESTAMP1, PACKAGE_NAME1, /*multiple=*/ true);
+ }
+
+ @After
+ public void closeDb() {
+ mDatabase.close();
+ BatteryStateDatabase.setBatteryStateDatabase(/*database=*/ null);
+ }
+
+ @Test
+ public void batteryStateDao_insertAll() throws Exception {
+ final List states = mBatteryStateDao.getAllAfter(TIMESTAMP1);
+ assertThat(states).hasSize(2);
+ // Verifies the queried battery states.
+ assertBatteryState(states.get(0), TIMESTAMP3, PACKAGE_NAME3);
+ assertBatteryState(states.get(1), TIMESTAMP2, PACKAGE_NAME2);
+ }
+
+ @Test
+ public void batteryStateDao_getCursorAfter() throws Exception {
+ final Cursor cursor = mBatteryStateDao.getCursorAfter(TIMESTAMP2);
+ assertThat(cursor.getCount()).isEqualTo(2);
+ assertThat(cursor.getColumnCount()).isEqualTo(CURSOR_COLUMN_SIZE);
+ // Verifies the queried first battery state.
+ cursor.moveToFirst();
+ assertThat(cursor.getString(4 /*packageName*/)).isEqualTo(PACKAGE_NAME3);
+ // Verifies the queried second battery state.
+ cursor.moveToNext();
+ assertThat(cursor.getString(4 /*packageName*/)).isEqualTo(PACKAGE_NAME2);
+ }
+
+ @Test
+ public void batteryStateDao_clearAllBefore() throws Exception {
+ mBatteryStateDao.clearAllBefore(TIMESTAMP2);
+
+ final List states = mBatteryStateDao.getAllAfter(0);
+ assertThat(states).hasSize(1);
+ // Verifies the queried battery state.
+ assertBatteryState(states.get(0), TIMESTAMP3, PACKAGE_NAME3);
+ }
+
+ @Test
+ public void batteryStateDao_clearAll() throws Exception {
+ assertThat(mBatteryStateDao.getAllAfter(0)).hasSize(3);
+ mBatteryStateDao.clearAll();
+ assertThat(mBatteryStateDao.getAllAfter(0)).isEmpty();
+ }
+
+ @Test
+ public void getInstance_createNewInstance() throws Exception {
+ BatteryStateDatabase.setBatteryStateDatabase(/*database=*/ null);
+ assertThat(BatteryStateDatabase.getInstance(mContext)).isNotNull();
+ }
+
+ @Test
+ public void getDistinctTimestampCount_returnsExpectedResult() {
+ assertThat(mBatteryStateDao.getDistinctTimestampCount(/*timestamp=*/ 0))
+ .isEqualTo(3);
+ assertThat(mBatteryStateDao.getDistinctTimestampCount(TIMESTAMP1))
+ .isEqualTo(2);
+ }
+
+ @Test
+ public void getDistinctTimestamps_returnsExpectedResult() {
+ final List timestamps =
+ mBatteryStateDao.getDistinctTimestamps(/*timestamp=*/ 0);
+
+ assertThat(timestamps).hasSize(3);
+ assertThat(timestamps).containsExactly(TIMESTAMP1, TIMESTAMP2, TIMESTAMP3);
+ }
+
+ private static void assertBatteryState(
+ BatteryState state, long timestamp, String packageName) {
+ assertThat(state.timestamp).isEqualTo(timestamp);
+ assertThat(state.packageName).isEqualTo(packageName);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateTest.java
new file mode 100644
index 00000000000..ef23c410d0c
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.fuelgauge.batteryusage.db;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Intent;
+import android.os.BatteryManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+/** Tests for {@link BatteryState}. */
+@RunWith(RobolectricTestRunner.class)
+public final class BatteryStateTest {
+ private static final int BATTERY_LEVEL = 45;
+ private static final int BATTERY_STATUS = BatteryManager.BATTERY_STATUS_FULL;
+ private static final int BATTERY_HEALTH = BatteryManager.BATTERY_HEALTH_COLD;
+
+ private Intent mBatteryIntent;
+
+ @Before
+ public void setUp() {
+ mBatteryIntent = new Intent(Intent.ACTION_BATTERY_CHANGED);
+ // Inserts the battery states into intent.
+ mBatteryIntent.putExtra(BatteryManager.EXTRA_LEVEL, BATTERY_LEVEL);
+ mBatteryIntent.putExtra(BatteryManager.EXTRA_STATUS, BATTERY_STATUS);
+ mBatteryIntent.putExtra(BatteryManager.EXTRA_HEALTH, BATTERY_HEALTH);
+ }
+
+ @Test
+ public void testBuilder_returnsExpectedResult() {
+ mBatteryIntent.putExtra(BatteryManager.EXTRA_SCALE, 100);
+ BatteryState state = create(mBatteryIntent);
+
+ // Verifies the app relative information.
+ assertThat(state.uid).isEqualTo(1001L);
+ assertThat(state.userId).isEqualTo(100L);
+ assertThat(state.appLabel).isEqualTo("Settings");
+ assertThat(state.packageName).isEqualTo("com.android.settings");
+ assertThat(state.isHidden).isTrue();
+ assertThat(state.bootTimestamp).isEqualTo(101L);
+ assertThat(state.timestamp).isEqualTo(100001L);
+ // Verifies the battery relative information.
+ assertThat(state.totalPower).isEqualTo(100);
+ assertThat(state.consumePower).isEqualTo(3);
+ assertThat(state.percentOfTotal).isEqualTo(10);
+ assertThat(state.foregroundUsageTimeInMs).isEqualTo(60000);
+ assertThat(state.backgroundUsageTimeInMs).isEqualTo(10000);
+ assertThat(state.drainType).isEqualTo(1);
+ assertThat(state.consumerType).isEqualTo(2);
+ assertThat(state.batteryLevel).isEqualTo(BATTERY_LEVEL);
+ assertThat(state.batteryStatus).isEqualTo(BATTERY_STATUS);
+ assertThat(state.batteryHealth).isEqualTo(BATTERY_HEALTH);
+ }
+
+ @Test
+ public void create_withoutBatteryScale_returnsStateWithInvalidLevel() {
+ BatteryState state = create(mBatteryIntent);
+ assertThat(state.batteryLevel).isEqualTo(-1);
+ }
+
+ private static BatteryState create(Intent intent) {
+ return BatteryState.newBuilder()
+ .setUid(1001L)
+ .setUserId(100L)
+ .setAppLabel("Settings")
+ .setPackageName("com.android.settings")
+ .setIsHidden(true)
+ .setBootTimestamp(101L)
+ .setTimestamp(100001L)
+ .setTotalPower(100f)
+ .setConsumePower(3f)
+ .setPercentOfTotal(10f)
+ .setForegroundUsageTimeInMs(60000)
+ .setBackgroundUsageTimeInMs(10000)
+ .setDrainType(1)
+ .setConsumerType(2)
+ .setBatteryIntent(intent)
+ .build();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
index 9043974d72f..c3b7821a07d 100644
--- a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
+++ b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
@@ -61,12 +61,9 @@ import com.android.settings.testutils.shadow.ShadowLockPatternUtils;
import com.android.settings.testutils.shadow.ShadowThreadUtils;
import com.android.settings.testutils.shadow.ShadowUserManager;
import com.android.settings.testutils.shadow.ShadowUtils;
-import com.android.settings.wifi.slice.WifiScanWorker;
-import com.android.settingslib.wifi.WifiTracker;
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -100,7 +97,6 @@ import java.util.Set;
@Config(shadows = {ShadowUserManager.class, ShadowUtils.class,
SlicesDatabaseAccessorTest.ShadowApplicationPackageManager.class,
ShadowBluetoothAdapter.class, ShadowLockPatternUtils.class,
- SettingsSliceProviderTest.ShadowWifiScanWorker.class,
SettingsSliceProviderTest.ShadowTheme.class})
public class SettingsSliceProviderTest {
@@ -123,6 +119,7 @@ public class SettingsSliceProviderTest {
private Context mContext;
private SettingsSliceProvider mProvider;
private ShadowPackageManager mPackageManager;
+
@Mock
private SliceManager mManager;
@@ -572,14 +569,6 @@ public class SettingsSliceProviderTest {
assertThat(pendingIntent).isEqualTo(settingsPendingIntent);
}
- @Test
- @Ignore
- public void bindSlice_wifiSlice_returnsWifiSlice() {
- final Slice wifiSlice = mProvider.onBindSlice(CustomSliceRegistry.WIFI_SLICE_URI);
-
- assertThat(wifiSlice.getUri()).isEqualTo(CustomSliceRegistry.WIFI_SLICE_URI);
- }
-
@Test
public void bindSlice_flashlightSlice_returnsFlashlightSlice() {
Settings.Secure.putInt(
@@ -603,32 +592,6 @@ public class SettingsSliceProviderTest {
mProvider.onSlicePinned(uri);
}
- @Test
- @Ignore
- public void onSlicePinned_backgroundWorker_started() {
- mProvider.onSlicePinned(CustomSliceRegistry.WIFI_SLICE_URI);
-
- verify(ShadowWifiScanWorker.getWifiTracker()).onStart();
- }
-
- @Test
- @Ignore
- public void onSlicePinned_backgroundWorker_stopped() {
- mProvider.onSlicePinned(CustomSliceRegistry.WIFI_SLICE_URI);
- mProvider.onSliceUnpinned(CustomSliceRegistry.WIFI_SLICE_URI);
-
- verify(ShadowWifiScanWorker.getWifiTracker()).onStop();
- }
-
- @Test
- @Ignore
- public void shutdown_backgroundWorker_closed() {
- mProvider.onSlicePinned(CustomSliceRegistry.WIFI_SLICE_URI);
- mProvider.shutdown();
-
- verify(ShadowWifiScanWorker.getWifiTracker()).onDestroy();
- }
-
@Test
@Config(qualifiers = "mcc998")
public void grantAllowlistedPackagePermissions_noAllowlist_shouldNotGrant() {
@@ -716,31 +679,6 @@ public class SettingsSliceProviderTest {
.build();
}
- @Implements(WifiScanWorker.class)
- public static class ShadowWifiScanWorker {
- private static WifiTracker mWifiTracker;
-
- @Implementation
- protected void onSlicePinned() {
- mWifiTracker = mock(WifiTracker.class);
- mWifiTracker.onStart();
- }
-
- @Implementation
- protected void onSliceUnpinned() {
- mWifiTracker.onStop();
- }
-
- @Implementation
- protected void close() {
- mWifiTracker.onDestroy();
- }
-
- static WifiTracker getWifiTracker() {
- return mWifiTracker;
- }
- }
-
@Implements(value = StrictMode.class)
public static class ShadowStrictMode {
diff --git a/tests/robotests/src/com/android/settings/testutils/BatteryTestUtils.java b/tests/robotests/src/com/android/settings/testutils/BatteryTestUtils.java
index e4e26d2c531..fa3ee10cc86 100644
--- a/tests/robotests/src/com/android/settings/testutils/BatteryTestUtils.java
+++ b/tests/robotests/src/com/android/settings/testutils/BatteryTestUtils.java
@@ -16,8 +16,21 @@
package com.android.settings.testutils;
+import android.content.Context;
import android.content.Intent;
import android.os.BatteryManager;
+import android.os.UserManager;
+
+import androidx.room.Room;
+
+import com.android.settings.fuelgauge.batteryusage.db.BatteryState;
+import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDao;
+import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDatabase;
+
+import com.google.common.collect.ImmutableList;
+
+import org.robolectric.Shadows;
+import org.robolectric.shadows.ShadowUserManager;
public class BatteryTestUtils {
@@ -37,6 +50,65 @@ public class BatteryTestUtils {
BatteryManager.BATTERY_STATUS_DISCHARGING);
}
+ /** Sets the work profile mode. */
+ public static void setWorkProfile(Context context) {
+ final UserManager userManager = context.getSystemService(UserManager.class);
+ Shadows.shadowOf(userManager).setManagedProfile(true);
+ // Changes out of the default system user so isSystemUser() returns false.
+ final int userId = 1001;
+ Shadows.shadowOf(userManager)
+ .addUser(userId, "name", /*flags=*/ ShadowUserManager.FLAG_PRIMARY);
+ Shadows.shadowOf(userManager).switchUser(userId);
+ }
+
+ /** Creates and sets up the in-memory {@link BatteryStateDatabase}. */
+ public static BatteryStateDatabase setUpBatteryStateDatabase(Context context) {
+ final BatteryStateDatabase inMemoryDatabase =
+ Room.inMemoryDatabaseBuilder(context, BatteryStateDatabase.class)
+ .allowMainThreadQueries()
+ .build();
+ BatteryStateDatabase.setBatteryStateDatabase(inMemoryDatabase);
+ return inMemoryDatabase;
+ }
+
+ /** Inserts a fake data into the database for testing. */
+ public static void insertDataToBatteryStateDatabase(
+ Context context, long timestamp, String packageName) {
+ insertDataToBatteryStateDatabase(context, timestamp, packageName, /*multiple=*/ false);
+ }
+
+ /** Inserts a fake data into the database for testing. */
+ public static void insertDataToBatteryStateDatabase(
+ Context context, long timestamp, String packageName, boolean multiple) {
+ final BatteryState state =
+ new BatteryState(
+ /*uid=*/ 1001L,
+ /*userId=*/ 100L,
+ /*appLabel=*/ "Settings",
+ packageName,
+ /*isHidden=*/ true,
+ /*bootTimestamp=*/ timestamp - 1,
+ timestamp,
+ /*zoneId=*/ "Europe/Paris",
+ /*totalPower=*/ 100f,
+ /*consumePower=*/ 0.3f,
+ /*percentOfTotal=*/ 10f,
+ /*foregroundUsageTimeInMs=*/ 60000,
+ /*backgroundUsageTimeInMs=*/ 10000,
+ /*drainType=*/ 1,
+ /*consumerType=*/ 2,
+ /*batteryLevel=*/ 31,
+ /*batteryStatus=*/ 0,
+ /*batteryHealth=*/ 0);
+ BatteryStateDao dao =
+ BatteryStateDatabase.getInstance(context).batteryStateDao();
+ if (multiple) {
+ dao.insertAll(ImmutableList.of(state));
+ } else {
+ dao.insert(state);
+ }
+ }
+
private static Intent getCustomBatteryIntent(int plugged, int level, int scale, int status) {
Intent intent = new Intent();
intent.putExtra(BatteryManager.EXTRA_PLUGGED, plugged);
diff --git a/tests/robotests/src/com/android/settings/testutils/FakeClock.java b/tests/robotests/src/com/android/settings/testutils/FakeClock.java
new file mode 100644
index 00000000000..946d0515340
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/testutils/FakeClock.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.testutils;
+
+import java.time.Clock;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.ZoneId;
+
+/** A fake {@link Clock} class for testing. */
+public final class FakeClock extends Clock {
+ private long mCurrentTimeMillis;
+
+ public FakeClock() {}
+
+ /** Sets the time in millis for {@link Clock#millis()} method. */
+ public void setCurrentTime(Duration duration) {
+ mCurrentTimeMillis = duration.toMillis();
+ }
+
+ @Override
+ public ZoneId getZone() {
+ throw new UnsupportedOperationException("unsupported!");
+ }
+
+ @Override
+ public Clock withZone(ZoneId zone) {
+ throw new UnsupportedOperationException("unsupported!");
+ }
+
+ @Override
+ public Instant instant() {
+ throw new UnsupportedOperationException("unsupported!");
+ }
+
+ @Override
+ public long millis() {
+ return mCurrentTimeMillis;
+ }
+}
diff --git a/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/MoreSettingsPreferenceControllerTest.java b/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/MoreSettingsPreferenceControllerTest.java
new file mode 100644
index 00000000000..503af46846d
--- /dev/null
+++ b/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/MoreSettingsPreferenceControllerTest.java
@@ -0,0 +1,99 @@
+/*
+ * 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.applications.specialaccess.notificationaccess;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.service.notification.NotificationListenerService;
+
+import androidx.preference.Preference;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.google.common.collect.ImmutableList;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class MoreSettingsPreferenceControllerTest {
+
+ Context mContext;
+ private MoreSettingsPreferenceController mController;
+ @Mock
+ PackageManager mPm;
+ final String mPkg = "pkg";
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = ApplicationProvider.getApplicationContext();
+
+ mController = new MoreSettingsPreferenceController(mContext);
+ mController.setPackage(mPkg);
+ mController.setPackageManager(mPm);
+
+ }
+
+ @Test
+ public void getAvailabilityStatus_available() {
+ ArgumentCaptor captor = ArgumentCaptor.forClass(Intent.class);
+ when(mPm.queryIntentActivities(captor.capture(), any())).thenReturn(
+ ImmutableList.of(mock(ResolveInfo.class)));
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ assertThat(captor.getValue().getPackage()).isEqualTo(mPkg);
+ assertThat(captor.getValue().getAction()).isEqualTo(Intent.ACTION_MAIN);
+ assertThat(captor.getValue().getCategories()).contains(
+ NotificationListenerService.INTENT_CATEGORY_SETTINGS_HOME);
+ }
+
+ @Test
+ public void getAvailabilityStatus_notAvailable() {
+ ArgumentCaptor captor = ArgumentCaptor.forClass(Intent.class);
+ when(mPm.queryIntentActivities(captor.capture(), any())).thenReturn(ImmutableList.of());
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
+ }
+
+ @Test
+ public void updateState() {
+ Preference preference = new Preference(mContext);
+ mController.updateState(preference);
+
+ assertThat(preference.getIntent().getPackage()).isEqualTo(mPkg);
+ assertThat(preference.getIntent().getAction()).isEqualTo(Intent.ACTION_MAIN);
+ assertThat(preference.getIntent().getCategories()).contains(
+ NotificationListenerService.INTENT_CATEGORY_SETTINGS_HOME);
+ }
+}
diff --git a/tests/unit/src/com/android/settings/network/UiccSlotUtilTest.java b/tests/unit/src/com/android/settings/network/UiccSlotUtilTest.java
index 4282b3e64c5..9a2c61167cb 100644
--- a/tests/unit/src/com/android/settings/network/UiccSlotUtilTest.java
+++ b/tests/unit/src/com/android/settings/network/UiccSlotUtilTest.java
@@ -638,6 +638,106 @@ public class UiccSlotUtilTest {
assertThat(testExcludedLogicalSlotIndex).isEqualTo(verifyExcludedLogicalSlotIndex);
}
+ @Test
+ public void isRemovableSimEnabled_noPsim_returnsFalse() {
+ when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(
+ oneSimSlotDeviceActiveEsim());
+
+ boolean testSlot = UiccSlotUtil.isRemovableSimEnabled(mTelephonyManager);
+
+ assertThat(testSlot).isFalse();
+ }
+
+ @Test
+ public void isRemovableSimEnabled_activeRemovableEsimAndInactivePsim_returnsFalse() {
+ when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(
+ twoSimSlotsDeviceActiveRemovableEsimInactivePsim());
+
+ boolean testSlot = UiccSlotUtil.isRemovableSimEnabled(mTelephonyManager);
+
+ assertThat(testSlot).isFalse();
+ }
+
+ @Test
+ public void isRemovableSimEnabled_activeRemovableEsimAndActivePsim_returnsTrue() {
+ when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(
+ twoSimSlotsDeviceActivePsimActiveRemovableEsim());
+
+ boolean testSlot = UiccSlotUtil.isRemovableSimEnabled(mTelephonyManager);
+
+ assertThat(testSlot).isTrue();
+ }
+
+ @Test
+ public void isRemovableSimEnabled_inactiveRemovableEsimAndActivePsim_returnsTrue() {
+ when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(
+ twoSimSlotsDeviceInactiveRemovableEsimActivePsim());
+
+ boolean testSlot = UiccSlotUtil.isRemovableSimEnabled(mTelephonyManager);
+
+ assertThat(testSlot).isTrue();
+ }
+
+ @Test
+ public void isRemovableSimEnabled_twoActiveRemovableEsimsAndInactivePsim_returnsFalse() {
+ when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(
+ twoSimSlotsDeviceTwoActiveRemovableEsimsInactivePsim());
+
+ boolean testSlot = UiccSlotUtil.isRemovableSimEnabled(mTelephonyManager);
+
+ assertThat(testSlot).isFalse();
+ }
+
+ @Test
+ public void isRemovableSimEnabled_oneActiveOneInactiveRemovableEsimActivePsim_returnsTrue() {
+ when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(
+ twoSimSlotsDeviceOneActiveOneInactiveRemovableEsimsActivePsim());
+
+ boolean testSlot = UiccSlotUtil.isRemovableSimEnabled(mTelephonyManager);
+
+ assertThat(testSlot).isTrue();
+ }
+
+ @Test
+ public void isRemovableSimEnabled_activePsim_returnsTrue() {
+ when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(
+ oneSimSlotDeviceActivePsim());
+
+ boolean testSlot = UiccSlotUtil.isRemovableSimEnabled(mTelephonyManager);
+
+ assertThat(testSlot).isTrue();
+ }
+
+ @Test
+ public void isRemovableSimEnabled_inactivePsim_returnsFalse() {
+ when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(
+ oneSimSlotDeviceinactivePsim());
+
+ boolean testSlot = UiccSlotUtil.isRemovableSimEnabled(mTelephonyManager);
+
+ assertThat(testSlot).isFalse();
+ }
+
+ @Test
+ public void isRemovableSimEnabled_activeEsimAndActivePsim_returnsTrue() {
+ when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(
+ twoSimSlotsDeviceActivePsimActiveEsim());
+
+ boolean testSlot = UiccSlotUtil.isRemovableSimEnabled(mTelephonyManager);
+
+ assertThat(testSlot).isTrue();
+ }
+
+ @Test
+ public void isRemovableSimEnabled_activeEsimAndInactivePsim_returnsFalse() {
+ when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(
+ twoSimSlotsDeviceInactivePsimActiveEsim());
+
+ boolean testSlot = UiccSlotUtil.isRemovableSimEnabled(mTelephonyManager);
+
+ assertThat(testSlot).isFalse();
+ }
+
private void compareTwoUiccSlotMappings(Collection testUiccSlotMappings,
Collection verifyUiccSlotMappings) {
assertThat(testUiccSlotMappings.size()).isEqualTo(verifyUiccSlotMappings.size());
@@ -792,6 +892,10 @@ public class UiccSlotUtilTest {
return new UiccSlotInfo[]{createUiccSlotInfo(true, false, 1, true)};
}
+ private UiccSlotInfo[] oneSimSlotDeviceinactivePsim() {
+ return new UiccSlotInfo[]{createUiccSlotInfo(false, true, -1, false)};
+ }
+
private UiccSlotInfo[] twoSimSlotsDeviceActivePsimActiveEsim() {
return new UiccSlotInfo[]{
createUiccSlotInfo(false, true, 0, true),
@@ -810,6 +914,30 @@ public class UiccSlotUtilTest {
createUiccSlotInfo(true, true, 1, true)};
}
+ private UiccSlotInfo[] twoSimSlotsDeviceActiveRemovableEsimInactivePsim() {
+ return new UiccSlotInfo[]{
+ createUiccSlotInfo(true, true, 0, true),
+ createUiccSlotInfo(false, true, -1, false)};
+ }
+
+ private UiccSlotInfo[] twoSimSlotsDeviceInactiveRemovableEsimActivePsim() {
+ return new UiccSlotInfo[]{
+ createUiccSlotInfo(true, true, -1, false),
+ createUiccSlotInfo(false, true, 0, true)};
+ }
+
+ private UiccSlotInfo[] twoSimSlotsDeviceTwoActiveRemovableEsimsInactivePsim() {
+ return new UiccSlotInfo[]{
+ createUiccSlotInfoForRemovableEsimMep(0, true, 1, true),
+ createUiccSlotInfo(false, true, -1, false)};
+ }
+
+ private UiccSlotInfo[] twoSimSlotsDeviceOneActiveOneInactiveRemovableEsimsActivePsim() {
+ return new UiccSlotInfo[]{
+ createUiccSlotInfoForRemovableEsimMep(1, true, -1, false),
+ createUiccSlotInfo(false, true, 0, true)};
+ }
+
private UiccSlotInfo[] twoSimSlotsDeviceActiveEsimActivePsim() {
return new UiccSlotInfo[]{
createUiccSlotInfo(true, false, 0, true),
@@ -872,4 +1000,20 @@ public class UiccSlotUtilTest {
logicalSlotIdx2 /* logicalSlotIdx */,
isActiveEsim2 /* isActive */)));
}
+
+ private UiccSlotInfo createUiccSlotInfoForRemovableEsimMep(int logicalSlotIdx1,
+ boolean isActiveEsim1, int logicalSlotIdx2, boolean isActiveEsim2) {
+ return new UiccSlotInfo(
+ true, /* isEuicc */
+ "123", /* cardId */
+ CARD_STATE_INFO_PRESENT, /* cardStateInfo */
+ true, /* isExtendApduSupported */
+ true, /* isRemovable */
+ Arrays.asList(
+ new UiccPortInfo("" /* iccId */, 0 /* portIdx */,
+ logicalSlotIdx1 /* logicalSlotIdx */, isActiveEsim1 /* isActive */),
+ new UiccPortInfo("" /* iccId */, 1 /* portIdx */,
+ logicalSlotIdx2 /* logicalSlotIdx */,
+ isActiveEsim2 /* isActive */)));
+ }
}