Merge "Add notifications for the concurrent display mode"
This commit is contained in:
@@ -207,4 +207,25 @@
|
||||
be set by OEMs for devices which use eUICCs. -->
|
||||
<integer-array name="non_removable_euicc_slots"></integer-array>
|
||||
|
||||
<!-- Device state identifiers and strings for system notifications. The string arrays must have
|
||||
the same length and order as the identifier array. -->
|
||||
<integer-array name="device_state_notification_state_identifiers">
|
||||
<!-- TODO(b/267231269) change to concurrent display identifier when ready -->
|
||||
<item>-1</item>
|
||||
</integer-array>
|
||||
<string-array name="device_state_notification_names">
|
||||
<item>@string/concurrent_display_notification_name</item>
|
||||
</string-array>
|
||||
<string-array name="device_state_notification_active_titles">
|
||||
<item>@string/concurrent_display_notification_active_title</item>
|
||||
</string-array>
|
||||
<string-array name="device_state_notification_active_contents">
|
||||
<item>@string/concurrent_display_notification_active_content</item>
|
||||
</string-array>
|
||||
<string-array name="device_state_notification_thermal_titles">
|
||||
<item>@string/concurrent_display_notification_thermal_title</item>
|
||||
</string-array>
|
||||
<string-array name="device_state_notification_thermal_contents">
|
||||
<item>@string/concurrent_display_notification_thermal_content</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
|
||||
@@ -6241,4 +6241,17 @@ ul.</string>
|
||||
<string name="mic_access_on_toast">Microphone is available</string>
|
||||
<!-- Toast message that is shown when the user mutes the microphone from the keyboard. [CHAR LIMIT=TOAST] -->
|
||||
<string name="mic_access_off_toast">Microphone is blocked</string>
|
||||
|
||||
<!-- Name of concurrent display notifications. [CHAR LIMIT=NONE] -->
|
||||
<string name="concurrent_display_notification_name">Dual screen</string>
|
||||
<!-- Title of concurrent display active notification. [CHAR LIMIT=NONE] -->
|
||||
<string name="concurrent_display_notification_active_title">Dual screen is on</string>
|
||||
<!-- Content of concurrent display active notification. [CHAR LIMIT=NONE] -->
|
||||
<string name="concurrent_display_notification_active_content"><xliff:g id="app_name" example="Camera app">%1$s</xliff:g> is using both displays to show content</string>
|
||||
<!-- Title of concurrent display thermal notification. [CHAR LIMIT=NONE] -->
|
||||
<string name="concurrent_display_notification_thermal_title">Device is too warm</string>
|
||||
<!-- Content of concurrent display thermal notification. [CHAR LIMIT=NONE] -->
|
||||
<string name="concurrent_display_notification_thermal_content">Dual Screen is unavailable because your phone is getting too warm</string>
|
||||
<!-- Text of device state notification turn off button. [CHAR LIMIT=NONE] -->
|
||||
<string name="device_state_notification_turn_off_button">Turn off</string>
|
||||
</resources>
|
||||
|
||||
@@ -4879,6 +4879,18 @@
|
||||
<java-symbol type="array" name="config_deviceStatesAvailableForAppRequests" />
|
||||
<java-symbol type="array" name="config_serviceStateLocationAllowedPackages" />
|
||||
<java-symbol type="integer" name="config_deviceStateRearDisplay"/>
|
||||
<java-symbol type="array" name="device_state_notification_state_identifiers"/>
|
||||
<java-symbol type="array" name="device_state_notification_names"/>
|
||||
<java-symbol type="array" name="device_state_notification_active_titles"/>
|
||||
<java-symbol type="array" name="device_state_notification_active_contents"/>
|
||||
<java-symbol type="array" name="device_state_notification_thermal_titles"/>
|
||||
<java-symbol type="array" name="device_state_notification_thermal_contents"/>
|
||||
<java-symbol type="string" name="concurrent_display_notification_name"/>
|
||||
<java-symbol type="string" name="concurrent_display_notification_active_title"/>
|
||||
<java-symbol type="string" name="concurrent_display_notification_active_content"/>
|
||||
<java-symbol type="string" name="concurrent_display_notification_thermal_title"/>
|
||||
<java-symbol type="string" name="concurrent_display_notification_thermal_content"/>
|
||||
<java-symbol type="string" name="device_state_notification_turn_off_button"/>
|
||||
<java-symbol type="bool" name="config_independentLockscreenLiveWallpaper"/>
|
||||
|
||||
<!-- For app language picker -->
|
||||
|
||||
@@ -25,6 +25,7 @@ import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STA
|
||||
import static com.android.server.devicestate.DeviceState.FLAG_CANCEL_OVERRIDE_REQUESTS;
|
||||
import static com.android.server.devicestate.OverrideRequest.OVERRIDE_REQUEST_TYPE_BASE_STATE;
|
||||
import static com.android.server.devicestate.OverrideRequest.OVERRIDE_REQUEST_TYPE_EMULATED_STATE;
|
||||
import static com.android.server.devicestate.OverrideRequestController.FLAG_THERMAL_CRITICAL;
|
||||
import static com.android.server.devicestate.OverrideRequestController.STATUS_ACTIVE;
|
||||
import static com.android.server.devicestate.OverrideRequestController.STATUS_CANCELED;
|
||||
import static com.android.server.devicestate.OverrideRequestController.STATUS_UNKNOWN;
|
||||
@@ -183,6 +184,9 @@ public final class DeviceStateManagerService extends SystemService {
|
||||
ActivityTaskManagerInternal.ScreenObserver mOverrideRequestScreenObserver =
|
||||
new OverrideRequestScreenObserver();
|
||||
|
||||
@NonNull
|
||||
private final DeviceStateNotificationController mDeviceStateNotificationController;
|
||||
|
||||
public DeviceStateManagerService(@NonNull Context context) {
|
||||
this(context, DeviceStatePolicy.Provider
|
||||
.fromResources(context.getResources())
|
||||
@@ -211,6 +215,13 @@ public final class DeviceStateManagerService extends SystemService {
|
||||
mDeviceStatePolicy.getDeviceStateProvider().setListener(mDeviceStateProviderListener);
|
||||
mBinderService = new BinderService();
|
||||
mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
|
||||
mDeviceStateNotificationController = new DeviceStateNotificationController(
|
||||
context, mHandler,
|
||||
() -> {
|
||||
synchronized (mLock) {
|
||||
mActiveOverride.ifPresent(mOverrideRequestController::cancelRequest);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -346,7 +357,8 @@ public final class DeviceStateManagerService extends SystemService {
|
||||
return mBinderService;
|
||||
}
|
||||
|
||||
private void updateSupportedStates(DeviceState[] supportedDeviceStates) {
|
||||
private void updateSupportedStates(DeviceState[] supportedDeviceStates,
|
||||
@DeviceStateProvider.SupportedStatesUpdatedReason int reason) {
|
||||
synchronized (mLock) {
|
||||
final int[] oldStateIdentifiers = getSupportedStateIdentifiersLocked();
|
||||
|
||||
@@ -370,7 +382,7 @@ public final class DeviceStateManagerService extends SystemService {
|
||||
return;
|
||||
}
|
||||
|
||||
mOverrideRequestController.handleNewSupportedStates(newStateIdentifiers);
|
||||
mOverrideRequestController.handleNewSupportedStates(newStateIdentifiers, reason);
|
||||
updatePendingStateLocked();
|
||||
|
||||
setRearDisplayStateLocked();
|
||||
@@ -596,7 +608,7 @@ public final class DeviceStateManagerService extends SystemService {
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private void onOverrideRequestStatusChangedLocked(@NonNull OverrideRequest request,
|
||||
@OverrideRequestController.RequestStatus int status) {
|
||||
@OverrideRequestController.RequestStatus int status, int flags) {
|
||||
if (request.getRequestType() == OVERRIDE_REQUEST_TYPE_BASE_STATE) {
|
||||
switch (status) {
|
||||
case STATUS_ACTIVE:
|
||||
@@ -616,10 +628,19 @@ public final class DeviceStateManagerService extends SystemService {
|
||||
switch (status) {
|
||||
case STATUS_ACTIVE:
|
||||
mActiveOverride = Optional.of(request);
|
||||
mDeviceStateNotificationController.showStateActiveNotificationIfNeeded(
|
||||
request.getRequestedState(), request.getUid());
|
||||
break;
|
||||
case STATUS_CANCELED:
|
||||
if (mActiveOverride.isPresent() && mActiveOverride.get() == request) {
|
||||
mActiveOverride = Optional.empty();
|
||||
mDeviceStateNotificationController.cancelNotification(
|
||||
request.getRequestedState());
|
||||
if ((flags & FLAG_THERMAL_CRITICAL) == FLAG_THERMAL_CRITICAL) {
|
||||
mDeviceStateNotificationController
|
||||
.showThermalCriticalNotificationIfNeeded(
|
||||
request.getRequestedState());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case STATUS_UNKNOWN: // same as default
|
||||
@@ -700,7 +721,7 @@ public final class DeviceStateManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
private void requestStateInternal(int state, int flags, int callingPid,
|
||||
private void requestStateInternal(int state, int flags, int callingPid, int callingUid,
|
||||
@NonNull IBinder token, boolean hasControlDeviceStatePermission) {
|
||||
synchronized (mLock) {
|
||||
final ProcessRecord processRecord = mProcessRecords.get(callingPid);
|
||||
@@ -721,8 +742,8 @@ public final class DeviceStateManagerService extends SystemService {
|
||||
+ " is not supported.");
|
||||
}
|
||||
|
||||
OverrideRequest request = new OverrideRequest(token, callingPid, state, flags,
|
||||
OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
|
||||
OverrideRequest request = new OverrideRequest(token, callingPid, callingUid,
|
||||
state, flags, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
|
||||
|
||||
// If we don't have the CONTROL_DEVICE_STATE permission, we want to show the overlay
|
||||
if (!hasControlDeviceStatePermission && mRearDisplayState != null
|
||||
@@ -762,7 +783,7 @@ public final class DeviceStateManagerService extends SystemService {
|
||||
}
|
||||
|
||||
private void requestBaseStateOverrideInternal(int state, int flags, int callingPid,
|
||||
@NonNull IBinder token) {
|
||||
int callingUid, @NonNull IBinder token) {
|
||||
synchronized (mLock) {
|
||||
final Optional<DeviceState> deviceState = getStateLocked(state);
|
||||
if (!deviceState.isPresent()) {
|
||||
@@ -782,8 +803,8 @@ public final class DeviceStateManagerService extends SystemService {
|
||||
+ " token: " + token);
|
||||
}
|
||||
|
||||
OverrideRequest request = new OverrideRequest(token, callingPid, state, flags,
|
||||
OVERRIDE_REQUEST_TYPE_BASE_STATE);
|
||||
OverrideRequest request = new OverrideRequest(token, callingPid, callingUid,
|
||||
state, flags, OVERRIDE_REQUEST_TYPE_BASE_STATE);
|
||||
mOverrideRequestController.addBaseStateRequest(request);
|
||||
}
|
||||
}
|
||||
@@ -953,11 +974,12 @@ public final class DeviceStateManagerService extends SystemService {
|
||||
@IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int mCurrentBaseState;
|
||||
|
||||
@Override
|
||||
public void onSupportedDeviceStatesChanged(DeviceState[] newDeviceStates) {
|
||||
public void onSupportedDeviceStatesChanged(DeviceState[] newDeviceStates,
|
||||
@DeviceStateProvider.SupportedStatesUpdatedReason int reason) {
|
||||
if (newDeviceStates.length == 0) {
|
||||
throw new IllegalArgumentException("Supported device states must not be empty");
|
||||
}
|
||||
updateSupportedStates(newDeviceStates);
|
||||
updateSupportedStates(newDeviceStates, reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1085,6 +1107,7 @@ public final class DeviceStateManagerService extends SystemService {
|
||||
@Override // Binder call
|
||||
public void requestState(IBinder token, int state, int flags) {
|
||||
final int callingPid = Binder.getCallingPid();
|
||||
final int callingUid = Binder.getCallingUid();
|
||||
// Allow top processes to request a device state change
|
||||
// If the calling process ID is not the top app, then we check if this process
|
||||
// holds a permission to CONTROL_DEVICE_STATE
|
||||
@@ -1099,7 +1122,8 @@ public final class DeviceStateManagerService extends SystemService {
|
||||
|
||||
final long callingIdentity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
requestStateInternal(state, flags, callingPid, token, hasControlStatePermission);
|
||||
requestStateInternal(state, flags, callingPid, callingUid, token,
|
||||
hasControlStatePermission);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(callingIdentity);
|
||||
}
|
||||
@@ -1124,6 +1148,7 @@ public final class DeviceStateManagerService extends SystemService {
|
||||
@Override // Binder call
|
||||
public void requestBaseStateOverride(IBinder token, int state, int flags) {
|
||||
final int callingPid = Binder.getCallingPid();
|
||||
final int callingUid = Binder.getCallingUid();
|
||||
getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
|
||||
"Permission required to control base state of device.");
|
||||
|
||||
@@ -1133,7 +1158,7 @@ public final class DeviceStateManagerService extends SystemService {
|
||||
|
||||
final long callingIdentity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
requestBaseStateOverrideInternal(state, flags, callingPid, token);
|
||||
requestBaseStateOverrideInternal(state, flags, callingPid, callingUid, token);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(callingIdentity);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,293 @@
|
||||
/*
|
||||
* 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.server.devicestate;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.hardware.devicestate.DeviceStateManager;
|
||||
import android.os.Handler;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
/**
|
||||
* Manages the user-visible device state notifications.
|
||||
*/
|
||||
class DeviceStateNotificationController extends BroadcastReceiver {
|
||||
private static final String TAG = "DeviceStateNotificationController";
|
||||
|
||||
@VisibleForTesting static final String INTENT_ACTION_CANCEL_STATE =
|
||||
"com.android.server.devicestate.INTENT_ACTION_CANCEL_STATE";
|
||||
@VisibleForTesting static final int NOTIFICATION_ID = 1;
|
||||
@VisibleForTesting static final String CHANNEL_ID = "DeviceStateManager";
|
||||
@VisibleForTesting static final String NOTIFICATION_TAG = "DeviceStateManager";
|
||||
|
||||
private final Context mContext;
|
||||
private final Handler mHandler;
|
||||
private final NotificationManager mNotificationManager;
|
||||
private final PackageManager mPackageManager;
|
||||
|
||||
// Stores the notification title and content indexed with the device state identifier.
|
||||
private final SparseArray<NotificationInfo> mNotificationInfos;
|
||||
|
||||
// The callback when a device state is requested to be canceled.
|
||||
private final Runnable mCancelStateRunnable;
|
||||
|
||||
DeviceStateNotificationController(@NonNull Context context, @NonNull Handler handler,
|
||||
@NonNull Runnable cancelStateRunnable) {
|
||||
this(context, handler, cancelStateRunnable, getNotificationInfos(context),
|
||||
context.getPackageManager(), context.getSystemService(NotificationManager.class));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
DeviceStateNotificationController(
|
||||
@NonNull Context context, @NonNull Handler handler,
|
||||
@NonNull Runnable cancelStateRunnable,
|
||||
@NonNull SparseArray<NotificationInfo> notificationInfos,
|
||||
@NonNull PackageManager packageManager,
|
||||
@NonNull NotificationManager notificationManager) {
|
||||
mContext = context;
|
||||
mHandler = handler;
|
||||
mCancelStateRunnable = cancelStateRunnable;
|
||||
mNotificationInfos = notificationInfos;
|
||||
mPackageManager = packageManager;
|
||||
mNotificationManager = notificationManager;
|
||||
mContext.registerReceiver(
|
||||
this,
|
||||
new IntentFilter(INTENT_ACTION_CANCEL_STATE),
|
||||
android.Manifest.permission.CONTROL_DEVICE_STATE,
|
||||
mHandler,
|
||||
Context.RECEIVER_NOT_EXPORTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the ongoing notification indicating that the device state is active. Does nothing if
|
||||
* the state does not have an active notification.
|
||||
*
|
||||
* @param state the active device state identifier.
|
||||
* @param requestingAppUid the uid of the requesting app used to retrieve the app name.
|
||||
*/
|
||||
void showStateActiveNotificationIfNeeded(int state, int requestingAppUid) {
|
||||
NotificationInfo info = mNotificationInfos.get(state);
|
||||
if (info == null || !info.hasActiveNotification()) {
|
||||
return;
|
||||
}
|
||||
String requesterApplicationLabel = getApplicationLabel(requestingAppUid);
|
||||
if (requesterApplicationLabel != null) {
|
||||
showNotification(
|
||||
info.name, info.activeNotificationTitle,
|
||||
String.format(info.activeNotificationContent, requesterApplicationLabel),
|
||||
true /* ongoing */
|
||||
);
|
||||
} else {
|
||||
Slog.e(TAG, "Cannot determine the requesting app name when showing state active "
|
||||
+ "notification. uid=" + requestingAppUid + ", state=" + state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the notification indicating that the device state is canceled due to thermal
|
||||
* critical condition. Does nothing if the state does not have a thermal critical notification.
|
||||
*
|
||||
* @param state the identifier of the device state being canceled.
|
||||
*/
|
||||
void showThermalCriticalNotificationIfNeeded(int state) {
|
||||
NotificationInfo info = mNotificationInfos.get(state);
|
||||
if (info == null || !info.hasThermalCriticalNotification()) {
|
||||
return;
|
||||
}
|
||||
showNotification(
|
||||
info.name, info.thermalCriticalNotificationTitle,
|
||||
info.thermalCriticalNotificationContent, false /* ongoing */
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the notification of the corresponding device state.
|
||||
*
|
||||
* @param state the device state identifier.
|
||||
*/
|
||||
void cancelNotification(int state) {
|
||||
if (!mNotificationInfos.contains(state)) {
|
||||
return;
|
||||
}
|
||||
mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(@NonNull Context context, @Nullable Intent intent) {
|
||||
if (intent != null) {
|
||||
if (INTENT_ACTION_CANCEL_STATE.equals(intent.getAction())) {
|
||||
mCancelStateRunnable.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a notification with the specified name, title, and content.
|
||||
*
|
||||
* @param name the name of the notification.
|
||||
* @param title the title of the notification.
|
||||
* @param content the content of the notification.
|
||||
* @param ongoing if true, display an ongoing (sticky) notification with a turn off button.
|
||||
*/
|
||||
private void showNotification(
|
||||
@NonNull String name, @NonNull String title, @NonNull String content, boolean ongoing) {
|
||||
final NotificationChannel channel = new NotificationChannel(
|
||||
CHANNEL_ID, name, NotificationManager.IMPORTANCE_HIGH);
|
||||
final Notification.Builder builder = new Notification.Builder(mContext, CHANNEL_ID)
|
||||
.setSmallIcon(R.drawable.ic_lock) // TODO(b/266833171) update icons when available.
|
||||
.setContentTitle(title)
|
||||
.setContentText(content)
|
||||
.setSubText(name)
|
||||
.setLocalOnly(true)
|
||||
.setOngoing(ongoing)
|
||||
.setCategory(Notification.CATEGORY_SYSTEM);
|
||||
|
||||
if (ongoing) {
|
||||
final Intent intent = new Intent(INTENT_ACTION_CANCEL_STATE)
|
||||
.setPackage(mContext.getPackageName());
|
||||
final PendingIntent pendingIntent = PendingIntent.getBroadcast(
|
||||
mContext, 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE);
|
||||
final Notification.Action action = new Notification.Action.Builder(
|
||||
null /* icon */,
|
||||
mContext.getString(R.string.device_state_notification_turn_off_button),
|
||||
pendingIntent)
|
||||
.build();
|
||||
builder.addAction(action);
|
||||
}
|
||||
|
||||
mNotificationManager.createNotificationChannel(channel);
|
||||
mNotificationManager.notify(NOTIFICATION_TAG, NOTIFICATION_ID, builder.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the resources for the notifications. The device state identifiers and strings are
|
||||
* stored in arrays. All the string arrays must have the same length and same order as the
|
||||
* identifier array.
|
||||
*/
|
||||
private static SparseArray<NotificationInfo> getNotificationInfos(Context context) {
|
||||
final SparseArray<NotificationInfo> notificationInfos = new SparseArray<>();
|
||||
|
||||
final int[] stateIdentifiers =
|
||||
context.getResources().getIntArray(
|
||||
R.array.device_state_notification_state_identifiers);
|
||||
final String[] names =
|
||||
context.getResources().getStringArray(R.array.device_state_notification_names);
|
||||
final String[] activeNotificationTitles =
|
||||
context.getResources().getStringArray(
|
||||
R.array.device_state_notification_active_titles);
|
||||
final String[] activeNotificationContents =
|
||||
context.getResources().getStringArray(
|
||||
R.array.device_state_notification_active_contents);
|
||||
final String[] thermalCriticalNotificationTitles =
|
||||
context.getResources().getStringArray(
|
||||
R.array.device_state_notification_thermal_titles);
|
||||
final String[] thermalCriticalNotificationContents =
|
||||
context.getResources().getStringArray(
|
||||
R.array.device_state_notification_thermal_contents);
|
||||
|
||||
if (stateIdentifiers.length != names.length
|
||||
|| stateIdentifiers.length != activeNotificationTitles.length
|
||||
|| stateIdentifiers.length != activeNotificationContents.length
|
||||
|| stateIdentifiers.length != thermalCriticalNotificationTitles.length
|
||||
|| stateIdentifiers.length != thermalCriticalNotificationContents.length
|
||||
) {
|
||||
throw new IllegalStateException(
|
||||
"The length of state identifiers and notification texts must match!");
|
||||
}
|
||||
|
||||
for (int i = 0; i < stateIdentifiers.length; i++) {
|
||||
int identifier = stateIdentifiers[i];
|
||||
if (identifier == DeviceStateManager.INVALID_DEVICE_STATE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
notificationInfos.put(
|
||||
identifier,
|
||||
new NotificationInfo(
|
||||
names[i], activeNotificationTitles[i], activeNotificationContents[i],
|
||||
thermalCriticalNotificationTitles[i],
|
||||
thermalCriticalNotificationContents[i])
|
||||
);
|
||||
}
|
||||
|
||||
return notificationInfos;
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper function to get app name (label) using the app uid.
|
||||
*
|
||||
* @param uid the uid of the app.
|
||||
* @return app name (label) if found, or null otherwise.
|
||||
*/
|
||||
@Nullable
|
||||
private String getApplicationLabel(int uid) {
|
||||
String packageName = mPackageManager.getNameForUid(uid);
|
||||
try {
|
||||
ApplicationInfo appInfo = mPackageManager.getApplicationInfo(
|
||||
packageName, PackageManager.ApplicationInfoFlags.of(0));
|
||||
return appInfo.loadLabel(mPackageManager).toString();
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A data class storing string resources of the notification of a device state.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static class NotificationInfo {
|
||||
public final String name;
|
||||
public final String activeNotificationTitle;
|
||||
public final String activeNotificationContent;
|
||||
public final String thermalCriticalNotificationTitle;
|
||||
public final String thermalCriticalNotificationContent;
|
||||
|
||||
NotificationInfo(String name, String activeNotificationTitle,
|
||||
String activeNotificationContent, String thermalCriticalNotificationTitle,
|
||||
String thermalCriticalNotificationContent) {
|
||||
|
||||
this.name = name;
|
||||
this.activeNotificationTitle = activeNotificationTitle;
|
||||
this.activeNotificationContent = activeNotificationContent;
|
||||
this.thermalCriticalNotificationTitle = thermalCriticalNotificationTitle;
|
||||
this.thermalCriticalNotificationContent = thermalCriticalNotificationContent;
|
||||
}
|
||||
|
||||
boolean hasActiveNotification() {
|
||||
return activeNotificationTitle != null && activeNotificationTitle.length() > 0;
|
||||
}
|
||||
|
||||
boolean hasThermalCriticalNotification() {
|
||||
return thermalCriticalNotificationTitle != null
|
||||
&& thermalCriticalNotificationTitle.length() > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,8 +19,12 @@ package com.android.server.devicestate;
|
||||
import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
|
||||
import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.IntRange;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* Responsible for providing the set of supported {@link DeviceState device states} as well as the
|
||||
* current device state.
|
||||
@@ -28,12 +32,42 @@ import android.annotation.IntRange;
|
||||
* @see DeviceStatePolicy
|
||||
*/
|
||||
public interface DeviceStateProvider {
|
||||
int SUPPORTED_DEVICE_STATES_CHANGED_DEFAULT = 0;
|
||||
|
||||
/**
|
||||
* Indicating that the supported device states changed callback is trigger for initial listener
|
||||
* registration.
|
||||
*/
|
||||
int SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED = 1;
|
||||
|
||||
/**
|
||||
* Indicating that the supported device states have changed because the thermal condition
|
||||
* returned to normal status from critical status.
|
||||
*/
|
||||
int SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_NORMAL = 2;
|
||||
|
||||
/**
|
||||
* Indicating that the supported device states have changed because of thermal critical
|
||||
* condition.
|
||||
*/
|
||||
int SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_CRITICAL = 3;
|
||||
|
||||
@IntDef(prefix = { "SUPPORTED_DEVICE_STATES_CHANGED_" }, value = {
|
||||
SUPPORTED_DEVICE_STATES_CHANGED_DEFAULT,
|
||||
SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED,
|
||||
SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_NORMAL,
|
||||
SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_CRITICAL
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@interface SupportedStatesUpdatedReason {}
|
||||
|
||||
/**
|
||||
* Registers a listener for changes in provider state.
|
||||
* <p>
|
||||
* It is <b>required</b> that {@link Listener#onSupportedDeviceStatesChanged(DeviceState[])} be
|
||||
* called followed by {@link Listener#onStateChanged(int)} with the initial values on successful
|
||||
* registration of the listener.
|
||||
* It is <b>required</b> that
|
||||
* {@link Listener#onSupportedDeviceStatesChanged(DeviceState[], int)} be called followed by
|
||||
* {@link Listener#onStateChanged(int)} with the initial values on successful registration of
|
||||
* the listener.
|
||||
*/
|
||||
void setListener(Listener listener);
|
||||
|
||||
@@ -53,18 +87,20 @@ public interface DeviceStateProvider {
|
||||
* to zero and there must always be at least one supported device state.
|
||||
*
|
||||
* @param newDeviceStates array of supported device states.
|
||||
* @param reason the reason for the supported device states change.
|
||||
*
|
||||
* @throws IllegalArgumentException if the list of device states is empty or if one of the
|
||||
* provided states contains an invalid identifier.
|
||||
*/
|
||||
void onSupportedDeviceStatesChanged(DeviceState[] newDeviceStates);
|
||||
void onSupportedDeviceStatesChanged(DeviceState[] newDeviceStates,
|
||||
@SupportedStatesUpdatedReason int reason);
|
||||
|
||||
/**
|
||||
* Called to notify the listener of a change in current device state. Required to be called
|
||||
* once on successful registration of the listener and then once on every subsequent change
|
||||
* in device state. Value must have been included in the set of supported device states
|
||||
* provided in the most recent call to
|
||||
* {@link #onSupportedDeviceStatesChanged(DeviceState[])}.
|
||||
* {@link #onSupportedDeviceStatesChanged(DeviceState[], int)}.
|
||||
*
|
||||
* @param identifier the identifier of the new device state.
|
||||
*
|
||||
|
||||
@@ -31,6 +31,7 @@ import java.lang.annotation.RetentionPolicy;
|
||||
final class OverrideRequest {
|
||||
private final IBinder mToken;
|
||||
private final int mPid;
|
||||
private final int mUid;
|
||||
private final int mRequestedState;
|
||||
@DeviceStateRequest.RequestFlags
|
||||
private final int mFlags;
|
||||
@@ -68,10 +69,11 @@ final class OverrideRequest {
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface OverrideRequestType {}
|
||||
|
||||
OverrideRequest(IBinder token, int pid, int requestedState,
|
||||
OverrideRequest(IBinder token, int pid, int uid, int requestedState,
|
||||
@DeviceStateRequest.RequestFlags int flags, @OverrideRequestType int requestType) {
|
||||
mToken = token;
|
||||
mPid = pid;
|
||||
mUid = uid;
|
||||
mRequestedState = requestedState;
|
||||
mFlags = flags;
|
||||
mRequestType = requestType;
|
||||
@@ -85,6 +87,10 @@ final class OverrideRequest {
|
||||
return mPid;
|
||||
}
|
||||
|
||||
int getUid() {
|
||||
return mUid;
|
||||
}
|
||||
|
||||
int getRequestedState() {
|
||||
return mRequestedState;
|
||||
}
|
||||
|
||||
@@ -59,6 +59,11 @@ final class OverrideRequestController {
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@interface RequestStatus {}
|
||||
|
||||
/**
|
||||
* A flag indicating that the status change was triggered by thermal critical status.
|
||||
*/
|
||||
static final int FLAG_THERMAL_CRITICAL = 1 << 0;
|
||||
|
||||
static String statusToString(@RequestStatus int status) {
|
||||
switch (status) {
|
||||
case STATUS_ACTIVE:
|
||||
@@ -106,7 +111,7 @@ final class OverrideRequestController {
|
||||
void addRequest(@NonNull OverrideRequest request) {
|
||||
OverrideRequest previousRequest = mRequest;
|
||||
mRequest = request;
|
||||
mListener.onStatusChanged(request, STATUS_ACTIVE);
|
||||
mListener.onStatusChanged(request, STATUS_ACTIVE, 0 /* flags */);
|
||||
|
||||
if (previousRequest != null) {
|
||||
cancelRequestLocked(previousRequest);
|
||||
@@ -116,7 +121,7 @@ final class OverrideRequestController {
|
||||
void addBaseStateRequest(@NonNull OverrideRequest request) {
|
||||
OverrideRequest previousRequest = mBaseStateRequest;
|
||||
mBaseStateRequest = request;
|
||||
mListener.onStatusChanged(request, STATUS_ACTIVE);
|
||||
mListener.onStatusChanged(request, STATUS_ACTIVE, 0 /* flags */);
|
||||
|
||||
if (previousRequest != null) {
|
||||
cancelRequestLocked(previousRequest);
|
||||
@@ -219,14 +224,17 @@ final class OverrideRequestController {
|
||||
* Notifies the controller that the set of supported states has changed. The controller will
|
||||
* notify the listener of all changes to request status as a result of this change.
|
||||
*/
|
||||
void handleNewSupportedStates(int[] newSupportedStates) {
|
||||
void handleNewSupportedStates(int[] newSupportedStates,
|
||||
@DeviceStateProvider.SupportedStatesUpdatedReason int reason) {
|
||||
boolean isThermalCritical =
|
||||
reason == DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_CRITICAL;
|
||||
if (mBaseStateRequest != null && !contains(newSupportedStates,
|
||||
mBaseStateRequest.getRequestedState())) {
|
||||
cancelCurrentBaseStateRequestLocked();
|
||||
cancelCurrentBaseStateRequestLocked(isThermalCritical ? FLAG_THERMAL_CRITICAL : 0);
|
||||
}
|
||||
|
||||
if (mRequest != null && !contains(newSupportedStates, mRequest.getRequestedState())) {
|
||||
cancelCurrentRequestLocked();
|
||||
cancelCurrentRequestLocked(isThermalCritical ? FLAG_THERMAL_CRITICAL : 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,7 +252,11 @@ final class OverrideRequestController {
|
||||
}
|
||||
|
||||
private void cancelRequestLocked(@NonNull OverrideRequest requestToCancel) {
|
||||
mListener.onStatusChanged(requestToCancel, STATUS_CANCELED);
|
||||
cancelRequestLocked(requestToCancel, 0 /* flags */);
|
||||
}
|
||||
|
||||
private void cancelRequestLocked(@NonNull OverrideRequest requestToCancel, int flags) {
|
||||
mListener.onStatusChanged(requestToCancel, STATUS_CANCELED, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -252,12 +264,16 @@ final class OverrideRequestController {
|
||||
* Notifies the listener of the canceled status as well.
|
||||
*/
|
||||
private void cancelCurrentRequestLocked() {
|
||||
cancelCurrentRequestLocked(0 /* flags */);
|
||||
}
|
||||
|
||||
private void cancelCurrentRequestLocked(int flags) {
|
||||
if (mRequest == null) {
|
||||
Slog.w(TAG, "Attempted to cancel a null OverrideRequest");
|
||||
return;
|
||||
}
|
||||
mStickyRequest = false;
|
||||
cancelRequestLocked(mRequest);
|
||||
cancelRequestLocked(mRequest, flags);
|
||||
mRequest = null;
|
||||
}
|
||||
|
||||
@@ -266,11 +282,15 @@ final class OverrideRequestController {
|
||||
* Notifies the listener of the canceled status as well.
|
||||
*/
|
||||
private void cancelCurrentBaseStateRequestLocked() {
|
||||
cancelCurrentBaseStateRequestLocked(0 /* flags */);
|
||||
}
|
||||
|
||||
private void cancelCurrentBaseStateRequestLocked(int flags) {
|
||||
if (mBaseStateRequest == null) {
|
||||
Slog.w(TAG, "Attempted to cancel a null OverrideRequest");
|
||||
return;
|
||||
}
|
||||
cancelRequestLocked(mBaseStateRequest);
|
||||
cancelRequestLocked(mBaseStateRequest, flags);
|
||||
mBaseStateRequest = null;
|
||||
}
|
||||
|
||||
@@ -284,12 +304,14 @@ final class OverrideRequestController {
|
||||
}
|
||||
|
||||
public interface StatusChangeListener {
|
||||
|
||||
/**
|
||||
* Notifies the listener of a change in request status. If a change within the controller
|
||||
* causes one request to become active and one to become either suspended or cancelled, this
|
||||
* method is guaranteed to be called with the active request first before the suspended or
|
||||
* cancelled request.
|
||||
*/
|
||||
void onStatusChanged(@NonNull OverrideRequest request, @RequestStatus int newStatus);
|
||||
void onStatusChanged(@NonNull OverrideRequest request, @RequestStatus int newStatus,
|
||||
int flags);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,12 +366,12 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider,
|
||||
}
|
||||
mListener = listener;
|
||||
}
|
||||
notifySupportedStatesChanged();
|
||||
notifySupportedStatesChanged(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED);
|
||||
notifyDeviceStateChangedIfNeeded();
|
||||
}
|
||||
|
||||
/** Notifies the listener that the set of supported device states has changed. */
|
||||
private void notifySupportedStatesChanged() {
|
||||
private void notifySupportedStatesChanged(@SupportedStatesUpdatedReason int reason) {
|
||||
List<DeviceState> supportedStates = new ArrayList<>();
|
||||
Listener listener;
|
||||
synchronized (mLock) {
|
||||
@@ -390,7 +390,7 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider,
|
||||
}
|
||||
|
||||
listener.onSupportedDeviceStatesChanged(
|
||||
supportedStates.toArray(new DeviceState[supportedStates.size()]));
|
||||
supportedStates.toArray(new DeviceState[supportedStates.size()]), reason);
|
||||
}
|
||||
|
||||
/** Computes the current device state and notifies the listener of a change, if needed. */
|
||||
@@ -688,7 +688,10 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider,
|
||||
if (isThermalStatusCriticalOrAbove != isPreviousThermalStatusCriticalOrAbove) {
|
||||
Slog.i(TAG, "Updating supported device states due to thermal status change."
|
||||
+ " isThermalStatusCriticalOrAbove: " + isThermalStatusCriticalOrAbove);
|
||||
notifySupportedStatesChanged();
|
||||
notifySupportedStatesChanged(
|
||||
isThermalStatusCriticalOrAbove
|
||||
? SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_CRITICAL
|
||||
: SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_NORMAL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -945,13 +945,15 @@ public final class DeviceStateManagerServiceTest {
|
||||
}
|
||||
|
||||
mListener = listener;
|
||||
mListener.onSupportedDeviceStatesChanged(mSupportedDeviceStates);
|
||||
mListener.onSupportedDeviceStatesChanged(mSupportedDeviceStates,
|
||||
SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED);
|
||||
mListener.onStateChanged(mSupportedDeviceStates[0].getIdentifier());
|
||||
}
|
||||
|
||||
public void notifySupportedDeviceStates(DeviceState[] supportedDeviceStates) {
|
||||
mSupportedDeviceStates = supportedDeviceStates;
|
||||
mListener.onSupportedDeviceStatesChanged(supportedDeviceStates);
|
||||
mListener.onSupportedDeviceStatesChanged(supportedDeviceStates,
|
||||
SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED);
|
||||
}
|
||||
|
||||
public void setState(int identifier) {
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* 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.server.devicestate;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Handler;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.ArgumentMatchers;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link DeviceStateNotificationController}.
|
||||
* <p/>
|
||||
* Run with <code>atest com.android.server.devicestate.DeviceStateNotificationControllerTest</code>.
|
||||
*/
|
||||
@Presubmit
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class DeviceStateNotificationControllerTest {
|
||||
|
||||
private static final int STATE_WITHOUT_NOTIFICATION = 1;
|
||||
private static final int STATE_WITH_ACTIVE_NOTIFICATION = 2;
|
||||
private static final int STATE_WITH_ACTIVE_AND_THERMAL_NOTIFICATION = 3;
|
||||
|
||||
private static final int VALID_APP_UID = 1000;
|
||||
private static final int INVALID_APP_UID = 2000;
|
||||
private static final String VALID_APP_NAME = "Valid app name";
|
||||
private static final String INVALID_APP_NAME = "Invalid app name";
|
||||
private static final String VALID_APP_LABEL = "Valid app label";
|
||||
|
||||
private static final String NAME_1 = "name1";
|
||||
private static final String TITLE_1 = "title1";
|
||||
private static final String CONTENT_1 = "content1:%1$s";
|
||||
private static final String NAME_2 = "name2";
|
||||
private static final String TITLE_2 = "title2";
|
||||
private static final String CONTENT_2 = "content2:%1$s";
|
||||
private static final String THERMAL_TITLE_2 = "thermal_title2";
|
||||
private static final String THERMAL_CONTENT_2 = "thermal_content2";
|
||||
|
||||
private DeviceStateNotificationController mController;
|
||||
|
||||
private final ArgumentCaptor<Notification> mNotificationCaptor = ArgumentCaptor.forClass(
|
||||
Notification.class);
|
||||
private final NotificationManager mNotificationManager = mock(NotificationManager.class);
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
Context context = InstrumentationRegistry.getInstrumentation().getContext();
|
||||
Handler handler = mock(Handler.class);
|
||||
PackageManager packageManager = mock(PackageManager.class);
|
||||
Runnable cancelStateRunnable = mock(Runnable.class);
|
||||
ApplicationInfo applicationInfo = mock(ApplicationInfo.class);
|
||||
|
||||
final SparseArray<DeviceStateNotificationController.NotificationInfo> notificationInfos =
|
||||
new SparseArray<>();
|
||||
notificationInfos.put(STATE_WITH_ACTIVE_NOTIFICATION,
|
||||
new DeviceStateNotificationController.NotificationInfo(
|
||||
NAME_1, TITLE_1, CONTENT_1,
|
||||
"", ""));
|
||||
notificationInfos.put(STATE_WITH_ACTIVE_AND_THERMAL_NOTIFICATION,
|
||||
new DeviceStateNotificationController.NotificationInfo(
|
||||
NAME_2, TITLE_2, CONTENT_2,
|
||||
THERMAL_TITLE_2, THERMAL_CONTENT_2));
|
||||
|
||||
when(packageManager.getNameForUid(VALID_APP_UID)).thenReturn(VALID_APP_NAME);
|
||||
when(packageManager.getNameForUid(INVALID_APP_UID)).thenReturn(INVALID_APP_NAME);
|
||||
when(packageManager.getApplicationInfo(eq(VALID_APP_NAME), ArgumentMatchers.any()))
|
||||
.thenReturn(applicationInfo);
|
||||
when(packageManager.getApplicationInfo(eq(INVALID_APP_NAME), ArgumentMatchers.any()))
|
||||
.thenThrow(new PackageManager.NameNotFoundException());
|
||||
when(applicationInfo.loadLabel(eq(packageManager))).thenReturn(VALID_APP_LABEL);
|
||||
|
||||
mController = new DeviceStateNotificationController(
|
||||
context, handler, cancelStateRunnable, notificationInfos,
|
||||
packageManager, mNotificationManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_activeNotification() {
|
||||
mController.showStateActiveNotificationIfNeeded(
|
||||
STATE_WITH_ACTIVE_NOTIFICATION, VALID_APP_UID);
|
||||
|
||||
// Verify that the notification manager is called with correct notification information.
|
||||
verify(mNotificationManager).notify(
|
||||
eq(DeviceStateNotificationController.NOTIFICATION_TAG),
|
||||
eq(DeviceStateNotificationController.NOTIFICATION_ID),
|
||||
mNotificationCaptor.capture());
|
||||
Notification notification = mNotificationCaptor.getValue();
|
||||
assertEquals(TITLE_1, notification.extras.getString(Notification.EXTRA_TITLE));
|
||||
assertEquals(String.format(CONTENT_1, VALID_APP_LABEL),
|
||||
notification.extras.getString(Notification.EXTRA_TEXT));
|
||||
assertEquals(Notification.FLAG_ONGOING_EVENT,
|
||||
notification.flags & Notification.FLAG_ONGOING_EVENT);
|
||||
|
||||
// Verify that the notification action is as expected.
|
||||
Notification.Action[] actions = notification.actions;
|
||||
assertEquals(1, actions.length);
|
||||
Notification.Action action = actions[0];
|
||||
assertEquals(DeviceStateNotificationController.INTENT_ACTION_CANCEL_STATE,
|
||||
action.actionIntent.getIntent().getAction());
|
||||
|
||||
// Verify that the notification is properly canceled.
|
||||
mController.cancelNotification(STATE_WITH_ACTIVE_NOTIFICATION);
|
||||
verify(mNotificationManager).cancel(
|
||||
DeviceStateNotificationController.NOTIFICATION_TAG,
|
||||
DeviceStateNotificationController.NOTIFICATION_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_thermalNotification() {
|
||||
// Verify that the active notification is created.
|
||||
mController.showStateActiveNotificationIfNeeded(
|
||||
STATE_WITH_ACTIVE_AND_THERMAL_NOTIFICATION, VALID_APP_UID);
|
||||
verify(mNotificationManager).notify(
|
||||
eq(DeviceStateNotificationController.NOTIFICATION_TAG),
|
||||
eq(DeviceStateNotificationController.NOTIFICATION_ID),
|
||||
mNotificationCaptor.capture());
|
||||
Notification notification = mNotificationCaptor.getValue();
|
||||
assertEquals(TITLE_2, notification.extras.getString(Notification.EXTRA_TITLE));
|
||||
assertEquals(String.format(CONTENT_2, VALID_APP_LABEL),
|
||||
notification.extras.getString(Notification.EXTRA_TEXT));
|
||||
assertEquals(Notification.FLAG_ONGOING_EVENT,
|
||||
notification.flags & Notification.FLAG_ONGOING_EVENT);
|
||||
Mockito.clearInvocations(mNotificationManager);
|
||||
|
||||
// Verify that the thermal critical notification is created.
|
||||
mController.showThermalCriticalNotificationIfNeeded(
|
||||
STATE_WITH_ACTIVE_AND_THERMAL_NOTIFICATION);
|
||||
verify(mNotificationManager).notify(
|
||||
eq(DeviceStateNotificationController.NOTIFICATION_TAG),
|
||||
eq(DeviceStateNotificationController.NOTIFICATION_ID),
|
||||
mNotificationCaptor.capture());
|
||||
notification = mNotificationCaptor.getValue();
|
||||
assertEquals(THERMAL_TITLE_2, notification.extras.getString(Notification.EXTRA_TITLE));
|
||||
assertEquals(THERMAL_CONTENT_2, notification.extras.getString(Notification.EXTRA_TEXT));
|
||||
assertEquals(0, notification.flags & Notification.FLAG_ONGOING_EVENT);
|
||||
|
||||
// Verify that the notification is canceled.
|
||||
mController.cancelNotification(STATE_WITH_ACTIVE_NOTIFICATION);
|
||||
verify(mNotificationManager).cancel(
|
||||
DeviceStateNotificationController.NOTIFICATION_TAG,
|
||||
DeviceStateNotificationController.NOTIFICATION_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_deviceStateWithoutNotification() {
|
||||
// Verify that no notification is created.
|
||||
mController.showStateActiveNotificationIfNeeded(
|
||||
STATE_WITHOUT_NOTIFICATION, VALID_APP_UID);
|
||||
verify(mNotificationManager, Mockito.never()).notify(
|
||||
eq(DeviceStateNotificationController.NOTIFICATION_TAG),
|
||||
eq(DeviceStateNotificationController.NOTIFICATION_ID),
|
||||
mNotificationCaptor.capture());
|
||||
}
|
||||
}
|
||||
@@ -58,7 +58,7 @@ public final class OverrideRequestControllerTest {
|
||||
|
||||
@Test
|
||||
public void addRequest() {
|
||||
OverrideRequest request = new OverrideRequest(new Binder(), 0 /* pid */,
|
||||
OverrideRequest request = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */,
|
||||
0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
|
||||
assertNull(mStatusListener.getLastStatus(request));
|
||||
|
||||
@@ -68,14 +68,14 @@ public final class OverrideRequestControllerTest {
|
||||
|
||||
@Test
|
||||
public void addRequest_cancelExistingRequestThroughNewRequest() {
|
||||
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
|
||||
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */,
|
||||
0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
|
||||
assertNull(mStatusListener.getLastStatus(firstRequest));
|
||||
|
||||
mController.addRequest(firstRequest);
|
||||
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
|
||||
|
||||
OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* pid */,
|
||||
OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */,
|
||||
1 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
|
||||
assertNull(mStatusListener.getLastStatus(secondRequest));
|
||||
|
||||
@@ -86,7 +86,7 @@ public final class OverrideRequestControllerTest {
|
||||
|
||||
@Test
|
||||
public void addRequest_cancelActiveRequest() {
|
||||
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
|
||||
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */,
|
||||
0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
|
||||
|
||||
mController.addRequest(firstRequest);
|
||||
@@ -100,7 +100,7 @@ public final class OverrideRequestControllerTest {
|
||||
|
||||
@Test
|
||||
public void addBaseStateRequest() {
|
||||
OverrideRequest request = new OverrideRequest(new Binder(), 0 /* pid */,
|
||||
OverrideRequest request = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */,
|
||||
0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
|
||||
assertNull(mStatusListener.getLastStatus(request));
|
||||
|
||||
@@ -110,14 +110,14 @@ public final class OverrideRequestControllerTest {
|
||||
|
||||
@Test
|
||||
public void addBaseStateRequest_cancelExistingBaseStateRequestThroughNewRequest() {
|
||||
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
|
||||
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */,
|
||||
0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
|
||||
assertNull(mStatusListener.getLastStatus(firstRequest));
|
||||
|
||||
mController.addBaseStateRequest(firstRequest);
|
||||
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
|
||||
|
||||
OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* pid */,
|
||||
OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */,
|
||||
1 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
|
||||
assertNull(mStatusListener.getLastStatus(secondRequest));
|
||||
|
||||
@@ -128,7 +128,7 @@ public final class OverrideRequestControllerTest {
|
||||
|
||||
@Test
|
||||
public void addBaseStateRequest_cancelActiveBaseStateRequest() {
|
||||
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
|
||||
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */,
|
||||
0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
|
||||
|
||||
mController.addBaseStateRequest(firstRequest);
|
||||
@@ -142,12 +142,13 @@ public final class OverrideRequestControllerTest {
|
||||
|
||||
@Test
|
||||
public void handleBaseStateChanged() {
|
||||
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
|
||||
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */,
|
||||
0 /* requestedState */,
|
||||
DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES /* flags */,
|
||||
OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
|
||||
|
||||
OverrideRequest baseStateRequest = new OverrideRequest(new Binder(), 0 /* pid */,
|
||||
0 /* uid */,
|
||||
0 /* requestedState */,
|
||||
0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
|
||||
|
||||
@@ -167,10 +168,11 @@ public final class OverrideRequestControllerTest {
|
||||
|
||||
@Test
|
||||
public void handleProcessDied() {
|
||||
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
|
||||
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */,
|
||||
0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
|
||||
|
||||
OverrideRequest baseStateRequest = new OverrideRequest(new Binder(), 0 /* pid */,
|
||||
0 /* uid */,
|
||||
1 /* requestedState */,
|
||||
0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
|
||||
|
||||
@@ -189,10 +191,11 @@ public final class OverrideRequestControllerTest {
|
||||
public void handleProcessDied_stickyRequests() {
|
||||
mController.setStickyRequestsAllowed(true);
|
||||
|
||||
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
|
||||
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */,
|
||||
0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
|
||||
|
||||
OverrideRequest baseStateRequest = new OverrideRequest(new Binder(), 0 /* pid */,
|
||||
0 /* uid */,
|
||||
1 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
|
||||
|
||||
mController.addRequest(firstRequest);
|
||||
@@ -211,10 +214,11 @@ public final class OverrideRequestControllerTest {
|
||||
|
||||
@Test
|
||||
public void handleNewSupportedStates() {
|
||||
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
|
||||
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */,
|
||||
1 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
|
||||
|
||||
OverrideRequest baseStateRequest = new OverrideRequest(new Binder(), 0 /* pid */,
|
||||
0 /* uid */,
|
||||
1 /* requestedState */,
|
||||
0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
|
||||
|
||||
@@ -224,18 +228,20 @@ public final class OverrideRequestControllerTest {
|
||||
mController.addBaseStateRequest(baseStateRequest);
|
||||
assertEquals(mStatusListener.getLastStatus(baseStateRequest).intValue(), STATUS_ACTIVE);
|
||||
|
||||
mController.handleNewSupportedStates(new int[]{0, 1});
|
||||
mController.handleNewSupportedStates(new int[]{0, 1},
|
||||
DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_DEFAULT);
|
||||
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
|
||||
assertEquals(mStatusListener.getLastStatus(baseStateRequest).intValue(), STATUS_ACTIVE);
|
||||
|
||||
mController.handleNewSupportedStates(new int[]{0});
|
||||
mController.handleNewSupportedStates(new int[]{0},
|
||||
DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_DEFAULT);
|
||||
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED);
|
||||
assertEquals(mStatusListener.getLastStatus(baseStateRequest).intValue(), STATUS_CANCELED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cancelOverrideRequestsTest() {
|
||||
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
|
||||
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */,
|
||||
1 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
|
||||
|
||||
mController.addRequest(firstRequest);
|
||||
@@ -250,7 +256,7 @@ public final class OverrideRequestControllerTest {
|
||||
private Map<OverrideRequest, Integer> mLastStatusMap = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void onStatusChanged(@NonNull OverrideRequest request, int newStatus) {
|
||||
public void onStatusChanged(@NonNull OverrideRequest request, int newStatus, int flags) {
|
||||
mLastStatusMap.put(request, newStatus);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,9 @@ package com.android.server.policy;
|
||||
|
||||
import static android.content.Context.SENSOR_SERVICE;
|
||||
|
||||
import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED;
|
||||
import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_CRITICAL;
|
||||
import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_NORMAL;
|
||||
import static com.android.server.policy.DeviceStateProviderImpl.DEFAULT_DEVICE_STATE;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
@@ -124,7 +127,8 @@ public final class DeviceStateProviderImplTest {
|
||||
DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
|
||||
provider.setListener(listener);
|
||||
|
||||
verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
|
||||
verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
|
||||
eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
|
||||
assertArrayEquals(new DeviceState[]{DEFAULT_DEVICE_STATE},
|
||||
mDeviceStateArrayCaptor.getValue());
|
||||
|
||||
@@ -151,7 +155,8 @@ public final class DeviceStateProviderImplTest {
|
||||
DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
|
||||
provider.setListener(listener);
|
||||
|
||||
verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
|
||||
verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
|
||||
eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
|
||||
final DeviceState[] expectedStates = new DeviceState[]{
|
||||
new DeviceState(1, "", 0 /* flags */),
|
||||
new DeviceState(2, "", 0 /* flags */) };
|
||||
@@ -183,7 +188,8 @@ public final class DeviceStateProviderImplTest {
|
||||
DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
|
||||
provider.setListener(listener);
|
||||
|
||||
verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
|
||||
verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
|
||||
eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
|
||||
final DeviceState[] expectedStates = new DeviceState[]{
|
||||
new DeviceState(1, "", DeviceState.FLAG_CANCEL_OVERRIDE_REQUESTS),
|
||||
new DeviceState(2, "", 0 /* flags */) };
|
||||
@@ -212,7 +218,8 @@ public final class DeviceStateProviderImplTest {
|
||||
DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
|
||||
provider.setListener(listener);
|
||||
|
||||
verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
|
||||
verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
|
||||
eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
|
||||
final DeviceState[] expectedStates = new DeviceState[]{
|
||||
new DeviceState(1, "", 0 /* flags */),
|
||||
new DeviceState(2, "", 0 /* flags */) };
|
||||
@@ -247,7 +254,8 @@ public final class DeviceStateProviderImplTest {
|
||||
DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
|
||||
provider.setListener(listener);
|
||||
|
||||
verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
|
||||
verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
|
||||
eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
|
||||
final DeviceState[] expectedStates = new DeviceState[]{
|
||||
new DeviceState(1, "", 0 /* flags */),
|
||||
new DeviceState(2, "CLOSED", 0 /* flags */) };
|
||||
@@ -265,7 +273,8 @@ public final class DeviceStateProviderImplTest {
|
||||
Mockito.clearInvocations(listener);
|
||||
|
||||
provider.notifyLidSwitchChanged(1, true /* lidOpen */);
|
||||
verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
|
||||
verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
|
||||
eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
|
||||
verify(listener).onStateChanged(mIntegerCaptor.capture());
|
||||
assertEquals(1, mIntegerCaptor.getValue().intValue());
|
||||
}
|
||||
@@ -336,7 +345,8 @@ public final class DeviceStateProviderImplTest {
|
||||
DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
|
||||
provider.setListener(listener);
|
||||
|
||||
verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
|
||||
verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
|
||||
eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
|
||||
assertArrayEquals(
|
||||
new DeviceState[]{
|
||||
new DeviceState(1, "CLOSED", 0 /* flags */),
|
||||
@@ -358,7 +368,8 @@ public final class DeviceStateProviderImplTest {
|
||||
|
||||
provider.onSensorChanged(event0);
|
||||
|
||||
verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
|
||||
verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
|
||||
eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
|
||||
verify(listener).onStateChanged(mIntegerCaptor.capture());
|
||||
assertEquals(3, mIntegerCaptor.getValue().intValue());
|
||||
|
||||
@@ -370,7 +381,8 @@ public final class DeviceStateProviderImplTest {
|
||||
|
||||
provider.onSensorChanged(event1);
|
||||
|
||||
verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
|
||||
verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
|
||||
eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
|
||||
verify(listener).onStateChanged(mIntegerCaptor.capture());
|
||||
assertEquals(2, mIntegerCaptor.getValue().intValue());
|
||||
|
||||
@@ -382,7 +394,8 @@ public final class DeviceStateProviderImplTest {
|
||||
|
||||
provider.onSensorChanged(event2);
|
||||
|
||||
verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
|
||||
verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
|
||||
eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
|
||||
verify(listener).onStateChanged(mIntegerCaptor.capture());
|
||||
assertEquals(1, mIntegerCaptor.getValue().intValue());
|
||||
}
|
||||
@@ -397,7 +410,8 @@ public final class DeviceStateProviderImplTest {
|
||||
DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
|
||||
provider.setListener(listener);
|
||||
|
||||
verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
|
||||
verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
|
||||
eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
|
||||
assertArrayEquals(
|
||||
new DeviceState[]{
|
||||
new DeviceState(1, "CLOSED", 0 /* flags */),
|
||||
@@ -410,12 +424,14 @@ public final class DeviceStateProviderImplTest {
|
||||
Mockito.clearInvocations(listener);
|
||||
|
||||
provider.onThermalStatusChanged(PowerManager.THERMAL_STATUS_MODERATE);
|
||||
verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
|
||||
verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
|
||||
eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
|
||||
Mockito.clearInvocations(listener);
|
||||
|
||||
// The THERMAL_TEST state should be disabled.
|
||||
provider.onThermalStatusChanged(PowerManager.THERMAL_STATUS_CRITICAL);
|
||||
verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
|
||||
verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
|
||||
eq(SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_CRITICAL));
|
||||
assertArrayEquals(
|
||||
new DeviceState[]{
|
||||
new DeviceState(1, "CLOSED", 0 /* flags */),
|
||||
@@ -426,7 +442,8 @@ public final class DeviceStateProviderImplTest {
|
||||
|
||||
// The THERMAL_TEST state should be re-enabled.
|
||||
provider.onThermalStatusChanged(PowerManager.THERMAL_STATUS_LIGHT);
|
||||
verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
|
||||
verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
|
||||
eq(SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_NORMAL));
|
||||
assertArrayEquals(
|
||||
new DeviceState[]{
|
||||
new DeviceState(1, "CLOSED", 0 /* flags */),
|
||||
@@ -468,7 +485,8 @@ public final class DeviceStateProviderImplTest {
|
||||
|
||||
provider.onSensorChanged(event2);
|
||||
|
||||
verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
|
||||
verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
|
||||
eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
|
||||
verify(listener, never()).onStateChanged(mIntegerCaptor.capture());
|
||||
}
|
||||
|
||||
@@ -513,7 +531,8 @@ public final class DeviceStateProviderImplTest {
|
||||
DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
|
||||
provider.setListener(listener);
|
||||
|
||||
verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
|
||||
verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
|
||||
eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
|
||||
assertArrayEquals(
|
||||
new DeviceState[]{
|
||||
new DeviceState(1, "CLOSED", 0 /* flags */),
|
||||
|
||||
Reference in New Issue
Block a user